zoukankan      html  css  js  c++  java
  • 断路器Hystrix(Feign)

    上一篇中我们讲了 断路器Hystrix(Ribbon) 本章讲解Feign+Hystrix已经Request请求传递,各种奇淫技巧….

    - Hystrix

    Hystrix支持回退概念:当 断路器 打开或运行错误时,执行默认的代码,给@FeignClient定义一个fallback属性,设置它实现回退的,还需要将您的实现类声明为Spring Bean。

    官方文档:http://cloud.spring.io/spring-cloud-static/Dalston.SR2/#spring-cloud-feign-hystrix-fallback

    - 准备工作

    1.启动Consul
    2.创建 battcn-provider 和 battcn-consumer 如果看了上一章的,可以直接copy代码复用

    - battcn-provider

    - pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    - ProviderApplication.java(有变化)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    @SpringBootApplication
    @EnableDiscoveryClient
    @RestController
    public class ProviderApplication {

    @Value("${spring.application.name}")
    String applicationName;

    public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
    }

    @GetMapping("/test1")
    public String test1() {
    return "My Name's :" + applicationName + " Email:1837307557@qq.com";
    }

    @GetMapping("/test2")
    public String test2() {
    System.out.println(1/0);
    return "hello error";
    }
    }

    - bootstrap.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    server:
    port: 8765

    spring:
    application:
    name: battcn-provider
    cloud:
    consul:
    host: localhost
    port: 8500
    enabled: true
    discovery:
    enabled: true
    prefer-ip-address: true

    - battcn-consumer

    - pom.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <dependencies>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    - ConsumerApplication

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @SpringCloudApplication
    @EnableFeignClients//开启 FeignClient支持
    public class ConsumerApplication {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
    return new RestTemplate();
    }

    public static void main(String[] args) {
    SpringApplication.run(ConsumerApplication.class, args);
    }

    }

    - HiClient

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package com.battcn.client;

    import org.springframework.cloud.netflix.feign.FeignClient;
    import org.springframework.stereotype.Component;
    import org.springframework.web.bind.annotation.GetMapping;

    /**
    * @author Levin
    * @date 2017-08-07.
    */
    @FeignClient(value = "battcn-provider",fallback = HiClient.HiClientFallback.class)
    public interface HiClient {

    @GetMapping("/test1")
    String test1();

    @GetMapping("/test2")
    String test2();

    @Component
    class HiClientFallback implements HiClient{
    @Override
    public String test1() {
    return "fallback....";
    }

    @Override
    public String test2() {
    return "fallback...";
    }
    }
    }

    - HiController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.battcn.controller;

    import com.battcn.client.HiClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;

    @RestController
    public class HiController {
    @Autowired
    HiClient hiClient;
    @GetMapping("/h1")
    public String hi() {
    return hiClient.test1();
    }
    @GetMapping("/h2")
    public String say() {
    return hiClient.test2();
    }
    }

    - bootstrap.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    server:
    port: 8766

    feign:
    hystrix:
    enabled: true #开启Feign Hystrix 支持

    spring:
    application:
    name: battcn-consumer
    cloud:
    consul:
    host: localhost
    port: 8500
    enabled: true
    discovery:
    enabled: true
    prefer-ip-address: true

    - 测试

    启动:battcn-provider

    启动:battcn-consumer

    访问:http://localhost:8500/ 显示如下代表服务注册成功

    查看注册中心查看注册中心

    访问:http://localhost:8766/h1

    1
    My Name's :battcn-provider Email:1837307557@qq.com	#正确情况

    访问:http://localhost:8766/h2

    1
    fallback...  	#错误情况,阻断输出fallback...

    - 解锁新姿势

    - 异常处理

    画图工具:https://www.processon.com/

    异常处理异常处理

    如果我们FeignClient服务都是内部的,在客户端抛出异常直接往最外层抛出,就不需要在消费者通过硬编码处理了,关键代码(完整代码看GIT)…

    battcn-provider 中异常处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception {
    if (e instanceof BattcnException) {
    BattcnException exception = (BattcnException) e;
    return exception.toErrorResponseEntity();
    }
    logger.error("服务器未知异常", e);
    rep.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    return new ErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器未知异常");
    }

    battcn-consumer 中异常处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception {
    if (e instanceof HystrixBadRequestException) {
    HystrixBadRequestException exception = (HystrixBadRequestException) e;
    rep.setStatus(HttpStatus.BAD_REQUEST.value());
    logger.info("[HystrixBadRequestException] - [" + exception.getMessage() + "]");
    JSONObject obj = JSON.parseObject(exception.getMessage());
    return new ErrorResponseEntity(obj.getInteger("customCode"), obj.getString("message"));
    }
    logger.error("服务器未知异常", e);
    rep.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    return new ErrorResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器未知异常");
    }

    Hystrix中只有,HystrixBadRequestException 是不会被计数,也不会进入阻断器,所以我们定义一个自己的错误解码器

    - FeignServiceErrorDecoder

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    package com.battcn.config;

    import com.netflix.hystrix.exception.HystrixBadRequestException;
    import feign.Response;
    import feign.Util;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import java.io.IOException;

    @Component
    public class FeignServiceErrorDecoder implements feign.codec.ErrorDecoder {
    static Logger LOGGER = LoggerFactory.getLogger(FeignServiceErrorDecoder.class);
    @Override
    public Exception decode(String methodKey, Response response) {
    try {
    if (response.status() >= 400 && response.status() <= 499) {
    String error = Util.toString(response.body().asReader());
    return new HystrixBadRequestException(error);
    }
    } catch (IOException e) {
    LOGGER.error("[Feign解析异常] - [{}]", e);
    }
    return feign.FeignException.errorStatus(methodKey, response);
    }
    }

    - 测试

    访问:http://localhost:8766/h1

    1
    My Name's :battcn-provider Email:1837307557@qq.com	#正确情况

    访问:http://localhost:8766/h2

    1
    {"customCode":400,"message":"请求错误"} 	#抛出异常,而不是进入阻断器

    关闭battcn-provider:http://localhost:8766/h1

    1
    fallback...  	#服务down机,阻断输出fallback...

    - Request 参数传递

    在开发中难免会有 服务之间 请求头传递比如Token,ID,因为我们使用的是FeignClient 的方式,那么我们无法获得HttpServletRequest的上下文,这个时候怎么办呢?通过硬编码是比较low的一种,接下来为各位看官带来简单粗暴的(也就知道这种,还有其它简单的方式欢迎交流….)

    参数传递参数传递

    - FeignRequest(consumer)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.battcn.config;

    import feign.RequestInterceptor;
    import feign.RequestTemplate;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import java.util.UUID;

    @Component
    public class FeignRequest implements RequestInterceptor {

    final Logger LOGGER = LoggerFactory.getLogger(this.getClass().getSimpleName());

    @Override
    public void apply(RequestTemplate requestTemplate) {
    //1.模拟获取request.header参数
    String token = UUID.randomUUID().toString().replace("-","").toUpperCase();
    LOGGER.info("传递的Token - [{}]",token);
    requestTemplate.header("token",token);//模拟将Token放入在 feign.Request对象中

    }
    }

    - HelloController(provider)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Value("${spring.application.name}")
    String applicationName;

    @Autowired
    HttpServletRequest request;

    @Override
    @GetMapping("/test1")
    public String test1() {
    return "My Name's :" + applicationName + " Token:"+request.getHeader("token");
    }

    - 测试

    日志结果日志结果

    结果:My Name's :battcn-provider Token:5588551D64C8478BA681A35892A03437 代表我们Token(HttpServletRequest)传递成功…

  • 相关阅读:
    MvvmLight:Command
    TreeView控件
    visual studio背景色
    公共语言运行时
    颜色列表
    自定义控件【旋转按钮,带圆角的边框】
    Loding Animation
    ComboBox前台xaml绑定数据
    Blend一些属性图解
    找到视觉树的方法
  • 原文地址:https://www.cnblogs.com/lywJ/p/10715572.html
Copyright © 2011-2022 走看看