spring Cloud
一、服务注册–Eureka
1、父子工程
父工程构建与一般spring boot项目构建工程无任何区别,不同的是构建完成后对pom.xml
的修改
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
//总的项目依赖,子工程将不再引入,而是用父工程替代
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
//父工程的相依信息
<groupId>com.springcloud</groupId>
<artifactId>cloud-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud-parent</name>
<description>Demo project for Spring Boot</description>
//声明改该项目是父工程
<packaging>pom</packaging>
//子工程
<modules>
<module>eureka-server-7001</module>
<module>comment-api</module>
<module>order-server-8001</module>
<module>order-server-8002</module>
<module>order-client</module>
</modules>
//版本声明
<properties>
<java.version>1.8</java.version>
<spring.cloud-version>Hoxton.SR8</spring.cloud-version>
<druid.version>1.2.3</druid.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<mysql.version>8.0.22</mysql.version>
</properties>
//版本控制,此时不会真正引入,只是版本管理,子工程将从这里引入需要的依赖版本
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
//父工程引入子工程都需要的依赖,子工程将不再重复引入
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
<!--spring -cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
子工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
//声明父工程
<parent>
<artifactId>cloud-parent</artifactId>
<groupId>com.springcloud</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>order-client</artifactId>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.springcloud</groupId>
<artifactId>comment-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、Eureka(停更维护)
spring 官方提供的注册中心,用于服务注册,spring cloud是基于spring boot的微服务一站式解决方案
与spring boot的版本兼容:
spring cloud | spring boot |
---|---|
Hoxton | 2.2.x, 2.3.x (Starting with SR5) |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
在使用中常使用spring boot父子工程来构建微服务项目,便于版本的统一管理
2.1、eureka注册中心(服务端)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
在新建的子项目中引入该依赖
在主类上开启注册服务
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
配置文件
server:
port: 7001
eureka:
instance:
#实例名称
hostname: eureka7001.com
client:
#不将自己注册进自己的注册中心
register-with-eureka: false
fetch-registry: false
service-url:
#其他注册中心(集群环境)
defaultZone: http://eureka7002.com:7002/eureka/
#单机环境
defaultZone: http://eureka7001.com:7001/eureka/
server:
#关闭自我保护
enable-self-preservation: false
服务端的控制器
@RestController
@PropertySource("classpath:application.yml")
public class OrderController {
@Autowired
OrdersService ordersService;
@Value("${server.port}")
private String port;
@GetMapping("/producer/getAllOrders")
public Map<String,Object> getAllOrders() {
Map<String,Object> map = new HashMap<>();
map.put("port",port);
map.put("res",ordersService.getAllOrders());
return map;
}
}
启动该子项目,访问http://eureka7001.com:7001
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9QHZFat-1615558639620)(https://i.loli.net/2020/12/07/Lkyl7sUtXAKOirT.png)]
说明配置成功
2.2、eureka(客户端)
引入客户端依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
主类开启服务
@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.springcloud.Mapper")
public class OrderServer8001Application {
public static void main(String[] args) {
SpringApplication.run(OrderServer8001Application.class, args);
}
}
配置文件
server:
port: 8001
spring:
application:
name: OrderServer
#eureka配置
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
一般服务端都是具体的微服务提供者,除了上述的配置,还有其他具体的业务类
访问http://eureka7001.com:7001
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xsn2KoZU-1615558639622)(https://i.loli.net/2020/12/07/qFXzl7jELVf9HSW.png)]
服务注册成功
2.3、eureka(服务的访问客户端)
服务的访问客户端也是微服务,要注册进注册中心,与以上配置相同
客户端访问服务时采用的是远程调用,最原始的方法使用RestTemplate
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced #开启负载均衡,默认是轮询
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
向容器注入RestTemplate对象
@RestController
public class OrderController {
@Autowired
RestTemplate restTemplate;
#服务地址
private static final String URL = "http://ORDERSERVER";
@GetMapping("/consumer/getAllOrders")
public Map<String,Object> getAllOrders(){
Map<String,Object> map = new HashMap<>();
map.put("flag","consumer");
#get请求路径为服务的controller请求
map.put("res",restTemplate.getForObject(URL+"/producer/getAllOrders",Map.class));
return map;
}
}
在浏览器中访问客户端,客户端会去访问具体的服务(注意端口的变化)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5lFWUKLN-1615558639625)(https://i.loli.net/2020/12/07/XUd6ir3mONwtG7g.png)]
2.4、自我保护
与zookeeper不同的是有服务下线、或暂时与注册中心断绝通信时eureka不会将服务删
注册中心还有zookeeper
、consul
、alibaba Nacos
等
3、zookeeper使用:
3.1、导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
3.2、yml
spring:
application:
name: sever-name
cloud:
zookeeper:
connect-string: zookeeper地址
3.3、主启动类
@SpringBootApplication
@EnableDiscoveryClient
4、consul使用:
4.1、导入依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
4.2、yml
spring:
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
4.3、主启动类:
@SpringBootApplication
@EnableDiscoveryClient
5、三个注册中心异同点:
组件名 | 语言 | CAP |
---|---|---|
Eureka | java | AP |
consul | go | CP |
zookeeper | java | CP |
6、服务发现DiscoveryClient
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/getServer")
public Map<String,Object> discovery(){
List<ServiceInstance> orderserver = discoveryClient.getInstances("ORDERSERVER");
Map<String,Object> map = new HashMap<>();
for (ServiceInstance serviceInstance : orderserver) {
map.put(serviceInstance.getServiceId()+serviceInstance.getPort(),serviceInstance.getUri());
}
return map;
}
主类上配置
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@MapperScan("com.springcloud.Mapper")
public class OrderServer8001Application {
public static void main(String[] args) {
SpringApplication.run(OrderServer8001Application.class, args);
}
}
访问服务发现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EVhYHwaV-1615558639628)(https://i.loli.net/2020/12/07/io4kwtvzM7lxsau.png)]
二、服务调用–openfeign
1、Ribbon(停更维护)
替换方案:spring cloud loadbalance
一个客户端负载均衡的工具(进程内的本地负载均衡)
主要功能:提供客户端的软件负载均衡算法与服务调用(负载均衡+RestTemplate)
原理:
-
ribbon会首先访问注册中心获取服务列表
-
根据访问规则即负载均衡算法,访问服务
-
负载均衡有自带的,也可有自己实现的
ribbon在最新的spring-cloud-eureka中做了整合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Upyg3BRa-1615558639630)(https://i.loli.net/2020/12/07/f3gAvmxlybzBDkG.png)]
还整合了spring cloud loadbalance
2、自带负载均衡算法
ribbon自带了7中负载均衡算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcc9nv6Q-1615558639632)(https://i.loli.net/2020/12/07/W8MuDGrflCowj2Z.png)]
有轮询、随机等
负载均衡方法替换
编写配置类(不能在主类所在包及子包中)
@Configuration
public class RibbonLB {
@Bean
public IRule MyRule(){
return new RandomRule();
}
}
主类
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="ORDERSERVER",configuration = RibbonLB.class)
public class OrderClientApplication {
public static void main(String[] args) {
SpringApplication.run(OrderClientApplication.class, args);
}
}
负载均衡原理:
-
获取服务地址列表
-
当前访问次数与服务总数取余,作为服务地址的下标
2、RestTemplate深入
RestTemplate常用有两请求方式即post、get
方法 | 返回类型 |
---|---|
getForObject | json |
postForObject | json |
getForEntity | response对象 |
postForEntity | response对象 |
3、Openfeign
一个声明式的web服务客户端(主要服务调用),整合Eureka、Ribbon支持负载均衡
(实际是对ribbon和RestTemplate的封装)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpiWHd6l-1615558639633)(https://i.loli.net/2020/12/07/XtAk2UqwPJSMIdh.png)]
3.1、引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
3.2、主类
@SpringBootApplication
@EnableFeignClients
public class OrderClient8081Application {
public static void main(String[] args) {
SpringApplication.run(OrderClient8081Application.class, args);
}
}
3.4、接口
@Component
@FeignClient(value = "ORDERSERVER")
public interface OrderFeignService {
@GetMapping("/producer/getAllOrders")
public Map<String,Object> getAllOrders();
}
OpenFeign使用与dobbu类似(面向接口),封装了RestTemplate和Ribbn
3.5、超时控制
openfeign默认等待1秒,超时则报错
若服务超时超过1秒,可进行超时延长
ribbon:
#读取时间
ReadTimeout: 5000
#建立连接时间
ConnectTimeout: 5000
3.6、日志增强
级别:
NONE: 默认
BASIC: 仅记录请求方法
HEABERS: 记录请求方法,包括请求、响应头消息
FULL: 记录请求方法,包括请求、响应头消息+正文响应体元数据
配置类
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
配置文件:
server:
port: 8081
logging:
level:
com.springcloud.Service.OrderFeignService: debug
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WNLRcPi4-1615558639635)(https://i.loli.net/2020/12/07/A6oXvBaynzxTkPm.png)]
三、服务降级–hystrix
3.1、断路器–hystrix(停更进维)
处理延迟、容错的开源库框架,长链路调用避免级联故障,保证在一个依赖出问题的情况下,不会导致整体服务失败,提高分布式系统的弹性
功能:
-
服务降级
在服务器忙等情况下,资源紧张,不上客户端等待立刻返回一个友好提示,fallback函数
触发:
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池\信号量打满也会导致服务降级(没有空闲线程资源)
降级的目的是为了解决整体项目的压力,而牺牲掉某一服务模块而采取的措施。
-
服务熔断
服务熔断是为了当服务中某段代码出现错误时,为了不影响其他客户端的请求而采取的措施
一般是出现错误,引起服务降级,进而服务熔断、最后链路恢复
根据错误率判断
-
限流:
在高并发场景下,为了方式大流量导致系统资源耗尽,进而发生系统奔溃、缓慢等对请求进行限制
3.2、hystrix在微服务中解决的问题
3.2.1、微服务中存在的问题
微服务有少则几种,多则几十、成百上千,在服务相互调用过程中,会产生多种问题来影响整体系统的运行性能等:
- 网络超时
- 网络原因导致服务调用超时(openfeign调用默认时间是2秒,超过则返回错误页面),则返回错误界面,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qYta4aM-1615558639637)(https://i.loli.net/2020/12/09/xh78erGM91s2oNL.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dQ77ui3G-1615558639638)(https://i.loli.net/2020/12/09/opkRyA5QTs7FHEq.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbUxr0vi-1615558639639)(https://i.loli.net/2020/12/09/XrhvF1xHSBcjpJz.png)]
-
流量过大,导致系统资源耗尽,等待超时发生以上错误
-
代码运行异常报错,导致系统异常
以上情况有多种方案:
-
使用openfeign实现超时控制,延长超时时间(延长时间难以确定)
-
使用hystrix处理:
- 服务降级
- 服务熔断
- 限流
为相应的方法提供托底方案(fallback)
-
服务端降级保护
1、引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
2、主类配置
@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.springcloud.Mapper")
@EnableCircuitBreaker
3、拖底函数
@GetMapping("/producer/timeOut/{id}")
//声明降级规则
@HystrixCommand(fallbackMethod = "timeOut2",commandProperties = {
//超时时间设置,表示该函数执行超出3秒\报错等,执行托底函数timeOut2
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value ="3000")
})
public Map<String,Object> timeOut(@PathVariable("id") Integer id) {
try {
Thread.sleep(id*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Map<String,Object> map = new HashMap<>();
map.put("port",port);
map.put("id",id);
map.put("res",ordersService.getAllOrders());
return map;
}
public Map<String,Object> timeOut2(@PathVariable("id") Integer id) {
Map<String,Object> map = new HashMap<>();
map.put("msg","超时请稍后访问!!!");
map.put("id",id);
return map;
}
测试[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oWxnfp7F-1615558639641)(https://i.loli.net/2020/12/10/PtoKGXSmQqF6ph5.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HZZPS76F-1615558639643)(https://i.loli.net/2020/12/10/3msyxS5FZUfMBJi.png)]
客户端访问时需要参考客户端feign远程访问的超时设置,feign的默认超时时间是2秒
超时问题中主要有几个参数
-
openfeign集成了ribbon所以有一个默认的连接时间
-
客户端开启feign.hystrix.ebable=true时,feignclient下所有的方法都会被Hystrix包裹,openfeign默认的连接时间是1秒
所以只要超过以上一个超时时间就会报超时异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c8LP4c5M-1615558639644)(https://i.loli.net/2020/12/10/B5LPKmXDWzoAeyw.png)]
所以在客户端时间超过设定的访问控制时间,也可进行客户端的服务降级,为客户端服务设置fallback托底函数
客户端降级保护
1、如上引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
客户端使用openfeign来进行服务的调用,其中继承了Hystrix的一些服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-37KowG3P-1615558639646)(https://i.loli.net/2020/12/10/GX6Iq7DY1yPoki4.png)]
2、主类配置
@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableHystrix
public class OrderClientHystrix8082Application {
public static void main(String[] args) {
SpringApplication.run(OrderClientHystrix8082Application.class, args);
}
}
3、接口编写
@GetMapping("/comsumer/timeOut/{id}")
@HystrixCommand(fallbackMethod = "timeOut2",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",
value = "3000") //3秒钟以内就是正常的业务逻辑
})
public Map<String,Object> timeOut(@PathVariable("id") String id){
Map<String,Object> map = new HashMap<>();
map.put("port",port);
map.put("res",orderFeignService.timeOut(id));
return map;
}
public Map<String,Object> timeOut2(@PathVariable("id") String id){
Map<String,Object> map = new HashMap<>();
map.put("meg","我是消费者8082,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)");
return map;
}
4、yml配置
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
feign:
hystrix:
enabled: true
测试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G3TiIrmB-1615558639647)(C:\Users\wjh\AppData\Roaming\Typora\typora-user-images\image-20201210145016920.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0c4Hqyt-1615558639648)(C:\Users\wjh\AppData\Roaming\Typora\typora-user-images\image-20201210145043056.png)]
小结:
客户端设定的超时时间是6秒,hystrix的测试时间是4秒,ribbon超过5秒,执行客户端的兜底函数
问题:
-
代码膨胀:
为每个方法提供一个兜底函数,会出现代码臃肿的情况
解决:
1> 创建默认统一的兜底函数
2> 为特殊的处理创建自定义的兜底函数
方式一:
1、在controller中编写全局统一的兜底函数
在controller类上添加注解
//payment_Global_FallbackMethod为兜底函数名(全局的兜底函数)
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
在controller中添加兜底方法
public String payment_Global_FallbackMethod(){
return "Global异常处理信息,请稍后再试,(┬_┬)";
}
在需要兜底的方法上添加注解
@HystrixCommand
2、自定义的兜底函数与之前相同
方式二 编写降级服务类提供降级的兜底函数,将业务函数与兜底函数分离
1> 实现接口该服务的接口
@Component
public class HystrixFallBackService implements OrderFeignService {
@Override
public Map<String, Object> getAllOrders() {
return null;
}
@Override
public Map<String, Object> timeOut(Integer id) {
Map<String,Object> map = new HashMap<>();
map.put("meg","我是消费者8082,对付支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,(┬_┬)");
return map;
}
}
2> yml配置
feign:
hystrix:
enabled: true #开启降级服务
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
3> 修改服务接口
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
4> 业务类
@RestController
@PropertySource("classpath:application.yml")
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderController {
@Autowired
OrderFeignService orderFeignService;
@Value("${server.port}")
private String port;
@GetMapping("/comsumer/getAllOrders")
@HystrixCommand
public Map<String,Object> getAllOrders() {
int s = 10/0;
Map<String,Object> map = new HashMap<>();
map.put("port",port);
map.put("res",orderFeignService.getAllOrders());
return map;
}
@GetMapping("/comsumer/timeOut/{id}")
@HystrixCommand(fallbackMethod = "timeOut",commandProperties ={
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value ="6000")
})
public Map<String,Object> timeOut(@PathVariable("id") Integer id){
Map<String,Object> map = new HashMap<>();
map.put("port",port);
map.put("res",orderFeignService.timeOut(id));
return map;
}
public Map<String,Object> payment_Global_FallbackMethod(){
Map<String,Object> map = new HashMap<>();
map.put("meg","全局兜底");
return map;
}
}
测试:
(只注解了HystirxCommend)的方法执行兜底函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SoLznCM4-1615558639650)(https://i.loli.net/2020/12/10/iQL9Ax1VeXDHhvm.png)]
指明了兜底函数则执行指定的兜底函数
熔断保护
1、什么是熔断
2、熔断配置
//服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间范围
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
if (id < 0){
throw new RuntimeException("*****id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
return "id 不能负数,请稍候再试,(┬_┬)/~~ id: " +id;
}
使用HystrixCommand来进行服务熔断,在设定的时间窗口,调用发生异常即服务调用的次数/设定的总调用的次数=失败率达到某一阈值,将发生服务的熔断
当成功率上升、失败率下降到阈值时服务恢复
多次错误,然后慢慢正确,发现刚开始不满足条件,就算是正确的访问地址也不能进行访问,需要慢慢的恢复链路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1o9GdRcv-1615558639652)(https://i.loli.net/2020/12/10/RWlETzi1C74AFex.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eNMVGRC4-1615558639653)(https://i.loli.net/2020/12/10/t3d8AwGXZDu9LEl.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8Nylcez-1615558639655)(https://i.loli.net/2020/12/10/aTUkQ3uOime6PMZ.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kKZi8SST-1615558639656)(https://i.loli.net/2020/12/10/m9su2ArKelcMPEO.png)]
四、服务网关–gateway
1、简介
gateway是spring cloud提供的为了简单有效的统一的管理路由的组件,可提供以下服务:
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
- …
2、gateway三大组件
-
路由Router
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
-
断言Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
-
过滤器Filter
使用过滤器,可以在请求被路由前或者之后对请求进行修改。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-scCbeTOo-1615558639658)(https://i.loli.net/2020/12/10/LUxGVMqfSskIXmu.png)]
3、工作原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Pvt4SZC-1615558639660)(https://i.loli.net/2020/12/11/elOd58N3iRmUYDT.png)]
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。
4、使用方式一(yml配置)
server:
port: 9001
spring:
application:
name: cloud-gateway-9001
cloud:
gateway:
routes:
- id: order-server_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/producer/getAllOrders #断言,路径相匹配的进行路由
- id: order-server_routh2
uri: http://localhost:8001
predicates:
- Path=/getServer #断言,路径相匹配的进行路由
#注册中心配置
eureka:
instance:
hostname: cloud-gateway-9001
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
5、使用方式二(编码类配置)
@Configuration
public class GateWayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_rote_atguigu", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
return routes.build();
}
}
6、动态路由
原理:
以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
spring:
application:
name: cloud-gateway-9001
cloud:
gateway:
discovery:
locator:
enabled: true #
routes:
- id: order-server_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://OrderServer #匹配后提供服务的路由地址
predicates:
- Path=/producer/getAllOrders #断言,路径相匹配的进行路由
- id: order-server_routh2
uri: lb://OrderServer
predicates:
- Path=/getServer #断言,路径相匹配的进行路由
测试:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s1HmqM88-1615558639662)(https://i.loli.net/2020/12/11/tqKNCs6IxfB4keA.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dHMmwA0b-1615558639663)(https://i.loli.net/2020/12/11/wU7RuLkgSt9fcnr.png)]
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能
7、Predicate断言
作用:
如果请求与断言相匹配则进行路由(所有的谓词都会匹配Http请求的不同属性)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARZJPOed-1615558639666)(https://i.loli.net/2020/12/11/NTmYgGMPh1UERWs.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-goed6nFP-1615558639667)(https://i.loli.net/2020/12/11/xPzwgEb3p7MyrsN.png)]
官网实例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w45J5Yyq-1615558639669)(https://i.loli.net/2020/12/11/rBMF6coivhmkXVR.png)]
说明:在如图在谓词After之后的时间点,该路由才可以匹配成功
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
8、路由过滤器Filter
修改HTTP请求和返回的HTTP响应,只能通过指定路由进行使用
内置了多种路由过滤器
return routes.build();
}
}
## 6、动态路由
原理:
以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
```yaml
spring:
application:
name: cloud-gateway-9001
cloud:
gateway:
discovery:
locator:
enabled: true #
routes:
- id: order-server_routh1 #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://OrderServer #匹配后提供服务的路由地址
predicates:
- Path=/producer/getAllOrders #断言,路径相匹配的进行路由
- id: order-server_routh2
uri: lb://OrderServer
predicates:
- Path=/getServer #断言,路径相匹配的进行路由
测试:
[外链图片转存中…(img-s1HmqM88-1615558639662)]
[外链图片转存中…(img-dHMmwA0b-1615558639663)]
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能
7、Predicate断言
作用:
如果请求与断言相匹配则进行路由(所有的谓词都会匹配Http请求的不同属性)
[外链图片转存中…(img-ARZJPOed-1615558639666)]
[外链图片转存中…(img-goed6nFP-1615558639667)]
官网实例:
[外链图片转存中…(img-w45J5Yyq-1615558639669)]
说明:在如图在谓词After之后的时间点,该路由才可以匹配成功
Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
8、路由过滤器Filter
修改HTTP请求和返回的HTTP响应,只能通过指定路由进行使用
内置了多种路由过滤器