上一篇中我们讲了 断路器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)传递成功…
- 004声明式服务调用Feign & 断路器Hystrix
1.POM配置 和普通Spring Boot工程相比,添加了Eureka Client.Feign.Hystrix依赖和Spring Cloud依赖管理 <dependencies> &l ...
- SpringCloud学习系列之三----- 断路器(Hystrix)和断路器监控(Dashboard)
前言 本篇主要介绍的是SpringCloud中的断路器(Hystrix)和断路器指标看板(Dashboard)的相关使用知识. SpringCloud Hystrix Hystrix 介绍 Netfl ...
- 【Spring Cloud笔记】 断路器-hystrix
在微服务架构中,一个微服务的超时失败可能导致瀑布式连锁反映,Spring Cloud Netflix 的断路器Hystrix通过自主反馈,防止了这种情况发生.下面介绍简单的断路器使用方法. [step ...
- spring cloud学习(五)断路器 Hystrix
断路器 Hystrix 断路器模式 (云计算设计模式) 断路器模式源于Martin Fowler的Circuit Breaker一文. 在分布式环境中,其中的应用程序执行访问远程资源和服务的操作,有可 ...
- 【Spring Cloud学习之六】断路器-Hystrix
环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 Spring Cloud 1.2 一.服务雪崩1.什么是服务雪崩分布式系统中经常会出现某个基础服务不可用造成整个系统不 ...
- Spring Cloud系列(四):断路器Hystrix
上一篇介绍了负载均衡的配置方法,做负载均衡是为了保证高可用性,但是有时候服务提供者挂掉了,比如服务A调用了服务B,服务B又调用了服务C,然后有一天服务C的所有节点都挂掉了,这时服务B就会因为C异常而在 ...
- SpringCloud IDEA 教学 (四) 断路器(Hystrix)
写在开始 在SpringCloud项目中,服务之间相互调用(RPC Remote Procedure Call —远程过程调用),处于调用链路底层的服务产生不可用情况时,请求会产生堆积使得服务器线程阻 ...
- 断路器Hystrix与Turbine集群监控-Spring Cloud学习第三天(非原创)
文章大纲 一.Hystrix基础介绍二.断路器Hystrix简单使用三.自定义Hystrix请求命令四.Hystrix的服务降级与异常处理五.Hystrix的请求缓存与请求合并六.Hystrix仪表盘 ...
- 断路器-Hystrix的深入了解
前言 高可用相关的技术以及架构,对于大型复杂的分布式系统,是非常重要的.而高可用架构中,非常重要的一个环节,就是如何将分布式系统中的各个服务打造成高可用的服务,从而足以应对分布式系统环境中的各种各样的 ...
随机推荐
- Java实现 LeetCode 655 输出二叉树(DFS+二分)
655. 输出二叉树 在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则: 行数 m 应当等于给定二叉树的高度. 列数 n 应当总是奇数. 根节点的值(以字符串格式给出)应当放在可放置的第一 ...
- Java实现 LeetCode 429 N叉树的层序遍历
429. N叉树的层序遍历 给定一个 N 叉树,返回其节点值的层序遍历. (即从左到右,逐层遍历). 例如,给定一个 3叉树 : 返回其层序遍历: [ [1], [3,2,4], [5,6] ] 说明 ...
- Java实现 LeetCode 217 存在重复元素
217. 存在重复元素 给定一个整数数组,判断是否存在重复元素. 如果任何值在数组中出现至少两次,函数返回 true.如果数组中每个元素都不相同,则返回 false. 示例 1: 输入: [1,2,3 ...
- Java实现 LeetCode 209 长度最小的子数组
209. 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组.如果不存在符合条件的连续子数组,返回 0. 示例: 输入: s = ...
- Java实现 LeetCode 94 二叉树的中序遍历
94. 二叉树的中序遍历 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? / ...
- Linux vi使用技巧
导入命令执行结果:r !命令,例如:导入已经存在的文件内容到当前文件 导入命令执行的结果到当前文件 定义快捷键,map 快捷键 触发命令,例如:map ^P I#<ESC>(使用CRTL+ ...
- java调用linux下的so库
1.编写java类 public class Abc { static { System.loadLibrary("abc"); } public native static St ...
- 循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用
在我前面随笔<循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用>里面曾经介绍过一些常规的界面组件的处理,主要介绍到单文本输入框.多文本框.下拉列 ...
- 温故知新-多线程-Cache Line存在验证
文章目录 简述 缓存行Cache Line 验证CacehLine存在? 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持 ...
- Spring新注解
@Configuration作用:指定当前类为一个配置类@ComponentScan作用:用于通过注释指定Spring在创建容器时要扫描的包 当配置类作为AnnotationCon ...