上一篇中我们讲了 断路器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实现 蓝桥杯VIP 算法训练 Hanoi问题
问题描述 如果将课本上的Hanoi塔问题稍做修改:仍然是给定N只盘子,3根柱子,但是允许每次最多移动相邻的M只盘子(当然移动盘子的数目也可以小于M),最少需要多少次? 例如N=5,M=2时,可以分别将 ...
- Java实现 蓝桥杯VIP 算法提高 最长公共子序列
算法提高 最长公共子序列 时间限制:1.0s 内存限制:256.0MB 问题描述 给定两个字符串,寻找这两个字串之间的最长公共子序列. 输入格式 输入两行,分别包含一个字符串,仅含有小写字母. 输出格 ...
- Linux 工作管理
把进程放入后台 进程后加&,这时,进程在后台是运行的 tar -zcf etc.tar.gz /etc & 在执行程序时,快速按下ctrl+z快捷键,这时,进程在后台是停止的 查看后台 ...
- DMR对讲机利用XLX网络联网通信
By 申建军 BD8SN 2018-9-29 本文适用于运行G4KLX DMRGateway的MMDVM热点和中继,目前国内绝大部分的热点用户都是使用pi-star镜像的MMDVM热点,均可按此设置. ...
- kvm的命令简单使用
virsh命令常用参数总结 参数 参数说明 基础操作 list 查看虚拟机列表,列出域 start 启动虚拟机,开始一个(以前定义的)非活跃的域 shutdown 关闭虚拟机,关闭一个域 dest ...
- 线性表 & 散列表
线性表: 数据排成一条线一样的机构,每个线性表上的数据最多只有前后两个方向, 包括 数组,链表,队列,栈. 非线性表 : 数据之间并不是简单的前后关系,有二叉树.图等. 散列表(基于 数组支持按照下标 ...
- <OPTEE>Trusted Application结构分析
最近又开始和Trusted Zone打起了交道,需要把Linaro开发的开源安全系统optee os移植到实验室的老板子上.不过导师要求我先开发一个应用,在普通环境和安全环境分别有一个程序,称为hos ...
- Springboot搭建Eureka并设置Eureka登录账号密码
Springboot搭建Eureka并设置Eureka登录账号密码 一.创建一个springboot项目 1.可以使用Spring Initializr,用浏览器打开http://start.spri ...
- 排队I(替换两层复杂for循环、避免TLE)
问题 C: 排队I(点击) 时间限制: 1 Sec 内存限制: 128 MB ...
- RESTORE FILELISTONLY FROM DISK ='D:\DataSQL\BakFile\pubs.bak' _ 数据库安装工具_连载_4
在查询分析器中执行: RESTORE FILELISTONLY FROM DISK = 'D:\DataSQL\BakFile\pubs.bak'根据查询类型判断Type(D.L),再取得对应的Lo ...