上一篇中我们讲了 断路器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 算法训练 黑色星期五
有些西方人比较迷信,如果某个月的13号正好是星期五,他们就会觉得不太吉利,用古人的说法,就是"诸事不宜".请你编写一个程序,统计出在某个特定的年份中,出现了多少次既是13号又是星期 ...
- Java实现 LeetCode 215. 数组中的第K个最大元素
215. 数组中的第K个最大元素 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 示例 1: 输入: [3,2,1,5,6 ...
- Java实现 LeetCode 126 单词接龙 II
126. 单词接龙 II 给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列.转换需遵循如下规则: ...
- Java实现堆排序问题(变治法)
问题描述 用基于变治法的堆排序算法对任意一组给定的数据进行排序 2.1 堆排序原理简介 堆可以定义为一颗二叉树,树的节点中包含键(每个节点是一个键),并且满足下面两个条件: (1)树的形状要求--这颗 ...
- Java实现完美洗牌算法
1 问题描述 有一个长度为2n的数组{a1,a2,a3,-,an,b1,b2,b3,-,bn},希望排序后变成{a1,b1,a2,b2,a3,b3,-,an,bn},请考虑有没有时间复杂度为O(n)而 ...
- PAT 福尔摩斯的约会
大侦探福尔摩斯接到一张奇怪的字条:我们约会吧! 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm.大侦探很快就明白了,字条上奇 ...
- OAuth + Security - 5 - Token存储升级(数据库、Redis)
PS:此文章为系列文章,建议从第一篇开始阅读. 在我们之前的文章中,我们当时获取到Token令牌时,此时的令牌时存储在内存中的,这样显然不利于我们程序的扩展,所以为了解决这个问题,官方给我们还提供了其 ...
- 有关指针 -> 和* 的重载
1, #include<iostream> #include<string> using namespace std; class test{ int i; public: t ...
- jreble备注
版本:2019.1.4 激活URL:http://jrebel.pyjuan.com/36931214-7bb6-42d4-afd7-26eb5628e004
- PHP 实现过滤参数字符的方法
//参数处理函数2function RepPostVar2($val){ if($val!=addslashes($val)) { exit(); } if(substr($val,-1)==&quo ...