上一篇中我们讲了 断路器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.cloudgroupId> <artifactId>spring-cloud-starter-consul-discoveryartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-actuatorartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> dependencies> |
- ProviderApplication.java&#xff08;有变化&#xff09;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | &#64;SpringBootApplication &#64;EnableDiscoveryClient &#64;RestController public class ProviderApplication {
&#64;Value("${spring.application.name}") String applicationName;
public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); }
&#64;GetMapping("/test1") public String test1() { return "My Name&#39;s :" &#43; applicationName &#43; " Email:1837307557&#64;qq.com"; }
&#64;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.cloudgroupId> <artifactId>spring-cloud-starter-feignartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-hystrixartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-actuatorartifactId> dependency> <dependency> <groupId>org.springframework.cloudgroupId> <artifactId>spring-cloud-starter-consul-discoveryartifactId> dependency> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-testartifactId> <scope>testscope> dependency> dependencies> |
- ConsumerApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | &#64;SpringCloudApplication &#64;EnableFeignClients |
- 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;
|
- 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;
&#64;RestController public class HiController { &#64;Autowired HiClient hiClient; &#64;GetMapping("/h1") public String hi() { return hiClient.test1(); } &#64;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 |
- 测试
启动&#xff1a;battcn-provider
启动&#xff1a;battcn-consumer
访问&#xff1a;http://localhost:8500/ 显示如下代表服务注册成功
查看注册中心
访问&#xff1a;http://localhost:8766/h1
1 | My Name&#39;s :battcn-provider Email:1837307557&#64;qq.com #正确情况 |
访问&#xff1a;http://localhost:8766/h2
1 | fallback... #错误情况&#xff0c;阻断输出fallback... |
- 解锁新姿势
- 异常处理
画图工具&#xff1a;https://www.processon.com/
异常处理
如果我们FeignClient
服务都是内部的&#xff0c;在客户端抛出异常直接往最外层抛出&#xff0c;就不需要在消费者通过硬编码处理了&#xff0c;关键代码&#xff08;完整代码看GIT&#xff09;…
battcn-provider
中异常处理
1 2 3 4 5 6 7 8 9 10 11 | &#64;ExceptionHandler(value &#61; Exception.class) &#64;ResponseBody public ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception { if (e instanceof BattcnException) { BattcnException exception &#61; (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 | &#64;ExceptionHandler(value &#61; Exception.class) &#64;ResponseBody public ErrorResponseEntity jsonErrorHandler(Exception e, HttpServletResponse rep) throws Exception { if (e instanceof HystrixBadRequestException) { HystrixBadRequestException exception &#61; (HystrixBadRequestException) e; rep.setStatus(HttpStatus.BAD_REQUEST.value()); logger.info("[HystrixBadRequestException] - [" &#43; exception.getMessage() &#43; "]"); JSONObject obj &#61; 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
中只有&#xff0c;HystrixBadRequestException
是不会被计数&#xff0c;也不会进入阻断器&#xff0c;所以我们定义一个自己的错误解码器
- 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;
&#64;Component public class FeignServiceErrorDecoder implements feign.codec.ErrorDecoder { static Logger LOGGER &#61; LoggerFactory.getLogger(FeignServiceErrorDecoder.class); &#64;Override public Exception decode(String methodKey, Response response) { try { if (response.status() >&#61; 400 && response.status() <&#61; 499) { String error &#61; Util.toString(response.body().asReader()); return new HystrixBadRequestException(error); } } catch (IOException e) { LOGGER.error("[Feign解析异常] - [{}]", e); } return feign.FeignException.errorStatus(methodKey, response); } } |
- 测试
访问&#xff1a;http://localhost:8766/h1
1 | My Name&#39;s :battcn-provider Email:1837307557&#64;qq.com #正确情况 |
访问&#xff1a;http://localhost:8766/h2
1 | {"customCode":400,"message":"请求错误"} #抛出异常&#xff0c;而不是进入阻断器 |
关闭battcn-provider&#xff1a;http://localhost:8766/h1
1 | fallback... #服务down机&#xff0c;阻断输出fallback... |
- Request 参数传递
在开发中难免会有 服务之间 请求头传递比如Token&#xff0c;ID&#xff0c;因为我们使用的是FeignClient
的方式&#xff0c;那么我们无法获得HttpServletRequest
的上下文&#xff0c;这个时候怎么办呢&#xff1f;通过硬编码是比较low
的一种&#xff0c;接下来为各位看官带来简单粗暴的&#xff08;也就知道这种&#xff0c;还有其它简单的方式欢迎交流….&#xff09;
参数传递
- FeignRequest&#xff08;consumer&#xff09;
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;
&#64;Component public class FeignRequest implements RequestInterceptor {
final Logger LOGGER &#61; LoggerFactory.getLogger(this.getClass().getSimpleName());
&#64;Override public void apply(RequestTemplate requestTemplate) { |
- HelloController&#xff08;provider&#xff09;
1 2 3 4 5 6 7 8 9 10 11 | &#64;Value("${spring.application.name}") String applicationName;
&#64;Autowired HttpServletRequest request;
&#64;Override &#64;GetMapping("/test1") public String test1() { return "My Name&#39;s :" &#43; applicationName &#43; " Token:"&#43;request.getHeader("token"); } |
- 测试
日志结果
结果&#xff1a;My Name&#39;s :battcn-provider Token:5588551D64C8478BA681A35892A03437
代表我们Token&#xff08;HttpServletRequest&#xff09;传递成功…