Hystrix 概述
Hystix 是 Netflix 开源的一个延迟和容错库,用于隔离访问远程服务、第三方库,防止出现级联失败(雪崩)。
pom依耐
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
• 雪崩:一个服务失败,导致整条链路的服务都失败的情形。
• Hystix 主要功能
- 隔离:用于隔离不同调用链之间相互不受影响
- 降级:封装友好的错误提示并返回
- 熔断:当请求错误率较高时,自动降级熔断
- 限流:合理分配每个调用的并发请求数量
隔离
1. 线程池隔离
将多个请求线程按照每个服务的配置,分发到不同的微服务去调用,这样就解决的,线程一窝蜂的同一微服务中,影响的雪崩
2. 信号量隔离
降级
异常,超时
在A调用C的时候,C服务发生了异常,或者执行时间过长,为了避免请求回不友好的错误信息或者线程不一直等待,我们提供一个友好的方法,该方法往往返回一个失败请求的响应结果集,如:服务器繁忙等等,使得请求响应能够快速获得,而且异常后是友好的提示,超时后能快速的返回响应,不占用线程.防止服务雪崩.
这个降级不但可以在服务提供方编写一个降级方法,也可以在服务消费方编写.
熔断
使用服务熔断,首先要导入pom依耐
然后在主启动开启熔断:@EnableCircuitBreaker//开启hystrix的断路器
在服务业务类上书写:
注意的是:不编写那些超时,请求次数,hystrix都会有默认的值,都默认支持熔断
//======服务熔断,providerCircuitBreakerMethod异常或者超时调用的方法
@HystrixCommand(fallbackMethod = "providerCircuitBreakerMethod",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
//sleepWindowInMilliseconds请求量阈值 默认失败次数20次
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
//监控时间,默认5000毫秒 就是打开状态与半开状态之间的休眠时间
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"),//时间窗口日期
//误差阈值百分比errorThresholdPercentage 失败率默认百分之50
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率60%达到多少跳闸(进行服务熔断)
})
public Result providerCircuitBreaker(@PathVariable("id") Integer id){
//制作异常,然后调用这个方法就会去调用配置的降级方法
if (id==0){
//id==0.就异常
int i = 1/0;
}
return new Result(true,"providerCircuitBreaker调用成功");
}
//熔断友好提示方法
@Override
public Result providerCircuitBreakerMethod(Integer id){
return new Result(false,"服务器繁忙,请稍候再试~~~熔断方法");
}
限流
服务限流:(flowLimit)
就好比秒杀商品等高并发操作,禁止一窝蜂的请求过来拥挤,大家排队,一秒钟处理N个,有序的进行
测试Hystrix的代码
搭建一个父工程
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fs</groupId>
<artifactId>study-springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>fs-server-eureka-7001</module>
<module>fs-provider-hystrix-8001</module>
<module>fs-consumer-hystrix-80</module>
</modules>
<!-- 作为父工程-->
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
<type>pom</type>
<!-- import 导入父工程的配置-->
<scope>import</scope>
</dependency>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR6</version>
<type>pom</type>
<!-- import 导入父工程的配置-->
<scope>import</scope>
</dependency>
<!-- spring-cloud-alibaba-dependencies 2.2.1.RELEASE -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- 整合MyBatis-->
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
</dependencies>
</project>
fs-server-eureka-7001(省略,请查阅上一偏博客,服务治理)
fs-provider-hystrix-8001 服务提供者
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-provider-hystrix-8001</artifactId>
<dependencies>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!--
第一种是:如果你的应用不会再需要返回xml的系列化格式,那么直接在pom.xml文件中将jackson-dataformat-xml这外包排除即可(如果其他包也进行了jackson-dataformat-xml的依赖引用也要视情况排除):
第二种是:不排除jackson-dataformat-xml包,而是直接在相应接口方法或Controller上明确指定将返回JSON格式的值:@GetMapping(value = "/user-instance", produces = MediaType.APPLICATION_PROBLEM_JSON_VALUE)
-->
<!-- 排除controller返回的格式为xml-->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- MyBatis-puls-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8001
spring:
application:
name: fs-provider-hystrix
datasource:
username: root
password: root
url: jdbc:mysql://192.168.93.132:3306/fs_springcloud
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #自定义数据源
# 配置eureka
eureka:
instance:
hostname: localhost # 主机名,写的是域名,本机在host文件中映射
prefer-ip-address: true # 将当前实例的ip注册到eureka server中.默认是false 注册主机名
ip-address: 127.0.0.1 # 修改instance-id显示
# # 修改instance-id显示,在eureka中的显示名称
# instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port}
# lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔
# lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务
client:
register-with-eureka: true # 将提供注册到注册eureka中心
fetch-registry: true # 从eureka上抓取已有的注册信息
service-url:
defaultZone: http://localhost:7001/eureka #,http://localhost2:7002/eureka,http://localhost3:7003/eureka # 注册中心地址
# 配置MyBatis-plus日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
FsProviderHystrix8001 主启动
注意:@EnableCircuitBreaker//开启hystrix的断路器
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//开启Eureka客户端
@EnableEurekaClient
@EnableCircuitBreaker//开启hystrix的断路器
public class FsProviderHystrix8001 {
public static void main(String[] args) {
SpringApplication.run(FsProviderHystrix8001.class,args);
}
}
PaymentServiceImpl 业务接口实现类
主要注解:@HystrixCommand
package com.fs.service.impl;
import com.fs.dao.PaymentDao;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import com.fs.service.PaymentService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.List;
@Service
public class PaymentServiceImpl implements PaymentService {
//注入dao
@Autowired
private PaymentDao paymentDao;
@Override
public List<Payment> findAll() {
//使用MyBatis提供的方法
return paymentDao.selectList(null);
}
/*
测试消费端配置的服务降级方法
*/
@Override
public Result testConsumer(Integer id) {
//制作异常,然后调用这个方法就会去调用配置的降级方法
if (id==0){
//id==0.就异常
int i = 1/0;
}
return new Result(true,"testConsumer调用成功");
}
/*
降级:
1.服务出现异常
2.服务调用超时,默认为1秒
*/
@Override
//@HystrixCommand一旦调用服务方法失败并抛出了错误信息后,会自动调用标注好的fallbackMethod()指定降级后调用的方法名称
@HystrixCommand(fallbackMethod = "providerFallbackMethod",commandProperties = {
//HystrixCommandProperties:这个类中有HystrixCommand注解的所有配置
//timeoutInMilliseconds规定这个方法在3秒内没有正常执行,就服务降级,方法异常会立马进行降级,不配置默认1秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
//熔断不配置默认是 默认失败次数20次 失败率默认百分之50 默认5000毫秒 就是打开状态与半开状态之间的休眠时间
})
public Result testFallbackMethod(Integer id){
//制作异常,然后调用这个方法就会去调用配置的降级方法
if (id==0){
//id==0.就异常
int i = 1/0;
}
return new Result(true,"testFallbackMethod调用成功");
}
//服务对应的降级方法
/*
1.方法的返回值需要和原方法一样
2,方法的参数需要和原方法一样
*/
@Override
public Result providerFallbackMethod(Integer id){
return new Result(false,"小二开小差中,请稍候再试~~~服务降级方法");
}
//======服务熔断,providerCircuitBreakerMethod异常或者超时调用的方法
@HystrixCommand(fallbackMethod = "providerCircuitBreakerMethod",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
//sleepWindowInMilliseconds请求量阈值 默认失败次数20次
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
//监控时间,默认5000毫秒 就是打开状态与半开状态之间的休眠时间
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000"),//时间窗口日期
//误差阈值百分比errorThresholdPercentage 失败率默认百分之50
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率60%达到多少跳闸(进行服务熔断)
})
public Result providerCircuitBreaker(@PathVariable("id") Integer id){
//制作异常,然后调用这个方法就会去调用配置的降级方法
if (id==0){
//id==0.就异常
int i = 1/0;
}
return new Result(true,"providerCircuitBreaker调用成功");
}
//熔断友好提示方法
@Override
public Result providerCircuitBreakerMethod(Integer id){
return new Result(false,"服务器繁忙,请稍候再试~~~熔断方法");
}
}
fs-consumer-hystrix-80 使用feign远程调用 服务消费端
pom.xml
== hystrix 因为OpenFeign集成了hystrix,所以不用导入hystrix的依耐==
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-consumer-hystrix-80</artifactId>
<dependencies>
<!-- hystrix 因为OpenFeign集成了hystrix,所以不用导入hystrix的依耐-->
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- eureka-Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- 排除controller返回的格式为xml-->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
主要的是加上:
因为feign默认是不开启hystrix的
# 开启feign对hystrix的支持,因为feign默认是不开启hystrix的
feign:
hystrix:
enabled: true
application.yml
server:
port: 80
spring:
application:
name: fs-consumer-hystrix-80
eureka:
client:
register-with-eureka: false # 消费者我目前的用途不需要将消费者注册到注册中心
fetch-registry: true # 从注册中心拉取服务
registry-fetch-interval-seconds: 30 # 默认30秒定时去注册中心拉取服务
service-url:
defaultZone: http://localhost:7001/eureka #,http://localhost2:7002/eureka,http://localhost3:7003/eureka # 注册中心地址
# 设置feign客户端超时时间(OpenFeign默认支持Ribbon) 不配置,默认1秒,因为feign底层是使用的Ribbon,Ribbon默认超时是1秒
ribbon:
#指的是建立链接后从服务器读取到可用资源所用的时间,等5秒
ReadTimeout: 5000
#指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间,等5秒
ConnectTimeout: 5000
logging:
level:
# root: debug # 开启springboot的debug的日志信息,不配置springboot默认是info
# com.fs: debug # 开启部分springboot的debug的日志信息
# feign日志以什么级别监控那个feign组件功能使用的接口,使用debug级别(只能记录debug级别),然后调用服务方法,在控制台就能看到详细debug信息
com.fs.springcloud.server.PaymentOpenFeignService: debug
# 开启feign对hystrix的支持,因为feign默认是不开启hystrix的
feign:
hystrix:
enabled: true
FsConsumerHystrix80 主启动
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//使用openFeign,激活并开启,feign集成了robbin同时集成了hystrix,自动默认轮询的负载均衡规则
@EnableFeignClients
@EnableEurekaClient
public class FsConsumerHystrix80 {
public static void main(String[] args) {
SpringApplication.run(FsConsumerHystrix80.class,args);
}
}
PaymentOpenFeignHystrix 定义 OpenFeign的接口
指定服务降级友好方法类:fallback = PaymentHystrixFallbackServiceImpl.class
package com.fs.feign;
import com.fs.config.OpenFeignConfig;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* feign的声明式接口,发起远程调用的,简化restTemplate
*
* 1.定义接口
* 2.接口上添加注解@FeignClient(value = "注册中心服务提供者名",configuration=定义的OpenFeign的日志类,fallback定义服务降级方法)
* 3.编写调用接口,接口的声明规则和提供方接口保持一致
* 4.去controller注入改接口对象,调用接口方法来完成远程调用
*/
@FeignClient(value = "fs-provider-hystrix",configuration = OpenFeignConfig.class,fallback = PaymentHystrixFallbackServiceImpl.class)
public interface PaymentOpenFeignHystrix {
//复制服务提供的controller方法,路径记得加上controller类上的路径
@RequestMapping("/payment/findAll")
Result<List<Payment>> findAll();
//测试time超时,由于我们在服务提供方的这个方法制作了sleep2秒,由于feign底层基于Ribbon,
//Ribbon默认超时时间为1秒,所以报错java.net.SocketTimeoutException: Read timed out
//解决办法在配置文件中配置Ribbon的超时时间
@RequestMapping("/payment/testTimeOut/{id}")
Result test(@PathVariable("id") Integer id);
}
PaymentHystrixFallbackServiceImpl 远程调用异常或者超时的服务降级友好方法类
package com.fs.feign;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Feign 客户端的降级处理类(feign自动集成hystrix,但是默认关闭,需要在yml中开启)
* 1. 定义类 实现 Feign 客户端接口
* 2. 使用@Component注解将该类的Bean加入SpringIOC容器
* 默认异常降级,默认等待超时1秒,默认异常占比百分之50,默认的半开时间5秒,默认10秒20次失败请求
*/
@Component
public class PaymentHystrixFallbackServiceImpl implements PaymentOpenFeignHystrix {
@Override
public Result<List<Payment>> findAll() {
return new Result(false,"fs-consumer-hystrix-80-findAll服务降级");
}
@Override
public Result test(Integer id) {
return new Result(false,"fs-consumer-hystrix-80-test服务降级");
}
}
OpenFeignConfig
package com.fs.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
OpenFeign的日志配置类
*/
@Configuration
public class OpenFeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//表示开启的是详细的feign日志,FULL表示最为详细的,点进去有4个
return Logger.Level.FULL;
}
//还需要在yml中配置feign日志已什么级别监控那个接口
}
PaymentController 消费端controller
package com.fs.controller;
import com.fs.feign.PaymentOpenFeignHystrix;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class PaymentController {
//注入OpenFeign接口
@Autowired
private PaymentOpenFeignHystrix paymentOpenFeign;
@RequestMapping("/payment/findAll")
public Result<List<Payment>> findAll(){
//调用OpenFeign接口
Result<List<Payment>> all = paymentOpenFeign.findAll();
return all;
}
//测试OpenFeign远程调用,服务降级方法
@RequestMapping("/payment/testTimeOut/{id}")
Result test(@PathVariable("id")Integer id){
Result test = paymentOpenFeign.test(id);
return test;
}
}
启动项目测试
先启动eureka注册中心:fs-server-eureka-7001
在启动服务提供端:fs-provider-hystrix-8001
在启动服务消费端:fs-consumer-hystrix-80
首先浏览器输入:http://localhost:7001/
然后测试服务提供方的业务是否能正常访问,我们直接访问被指定降级的业务方法
然后测试服务消费端配置的服务降级接口
Hystrix 熔断监控
Gateway 网关
网关概述
• 网关旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
• 在微服务架构中,不同的微服务可以有不同的网络地址,各个微服务之间通过互相调用完成用户请求,客户端可能通过调用N个微服务的接口完成一个用户请求。
• 存在的问题:
• 客户端多次请求不同的微服务,增加客户端的复杂性
• 认证复杂,每个服务都要进行认证
• http请求不同服务次数增加,性能不高
• 网关就是系统的入口,封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、缓存、负载均衡、流量管控、路由转发等
• 在目前的网关解决方案里,有Nginx+ Lua、Netflix Zuul 、Spring Cloud Gateway等等
什么是Gateway
- 定义:前端统一访问微服务的人口
- 功能:
- 认证
- 鉴权
- 日志
- 监控
- 缓存
- 负载均衡
- 流量控制
- 事实:
- Gateway自动集成Ribbon,且默认开启相关功能
Gateway 网关路由配置
Gateway 网关路由配置 – 静态路由
# 网关配置
gateway:
discovery:
locator:
enabled: true #使用情况少,不常用(了解) 开启从注册中心动态创建路由的功能,利用微服务进行路由,请求路径前可以添加微服务名称
lower-case-service-id: true #(了解) 允许请求路径的微服务名称为小写
routes:
- id: payment_routh #payment_routh 路由的id,若不配置,默认是UUID,没有固定规则但要求唯一
uri: http://localhost:8001 # 静态路由 这样配置是死的,不好 匹配后提供服务的路由地址
Gateway 网关路由配置 – 动态路由
• 引入eureka-client配置
• 修改uri属性:uri: lb://服务名称
# 网关配置
gateway:
discovery:
locator:
enabled: true #使用情况少,不常用(了解) 开启从注册中心动态创建路由的功能,利用微服务进行路由,请求路径前可以添加微服务名称
lower-case-service-id: true #(了解) 允许请求路径的微服务名称为小写
routes:
- id: payment_routh #payment_routh 路由的id,若不配置,默认是UUID,没有固定规则但要求唯一
uri: lb://FS-PROVIDER # 动态路由 匹配提供服务的路由地址,达到路由是去服务中心找端口,而不是写死:原理是使用自动集成Ribbon,使用默认配置
Gateway 网关路由配置 – 微服务名称配置
# 网关配置
gateway:
discovery:
locator:
enabled: true #使用情况少,不常用(了解) 开启从注册中心动态创建路由的功能,利用微服务进行路由,请求路径前可以添加微服务名称
lower-case-service-id: true #(了解) 允许请求路径的微服务名称为小写
Gateway 过滤器
局部过滤器配置
predicates 断言
spring:
application:
name: fs-gateway
cloud:
# 网关配置
gateway:
discovery:
locator:
enabled: true #使用情况少,不常用(了解) 开启从注册中心动态创建路由的功能,利用微服务进行路由,请求路径前可以添加微服务名称
lower-case-service-id: true #(了解) 允许请求路径的微服务名称为小写
# 路由配置:转发规则
routes:
- id: payment_routh #payment_routh 路由的id,若不配置,默认是UUID,没有固定规则但要求唯一
# uri: http://localhost:8001 # 静态路由 这样配置是死的,不好 匹配后提供服务的路由地址
uri: lb://FS-PROVIDER # 动态路由 匹配提供服务的路由地址,达到路由是去服务中心找端口,而不是写死:原理是使用自动集成Ribbon,使用默认配置
filters:
# 下面是官方给定的局部过滤器,我们一般还是使用自定义过滤器
# - AddRequestParameter=username,xiaofu # 通过过滤工厂会在匹配的请求头上加上一对请求头参数,名称为username值为xiaofu
predicates: # predicates就是为了实现一组匹配规则,让请求过来找到对应的route进行处理
- Path=/payment/get/** # 断言,路径相匹配的地址 注意Path要大写
# - Cookie=username,xiaofu #携带cookie访问,断言是否有,有就允许访问,这里写死了 使用cmd命令curl携带cookie测试 curl http://localhost:9527/payment/get/2 --cookie "username=xiaofu"
# 可以配置多个
# - id: payment_routh02 #payment_routh02 路由的id,没有固定规则但要求唯一
# uri: http://localhost:8001 #匹配后提供服务的路由地址
# predicates:
# - Path=/payment/lb/** # 断言,路径相匹配的地址
全局过滤器在下面的案列中
使用Gateway
1.导入依耐
2.编写application.yml配置
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
搭建Gateway工程
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>shangguiguSpringCloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs_gateway_gateway9527</artifactId>
<dependencies>
<!-- gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 基础配置-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
application.yml
配置
- 路由配置
- id:自定义服务名称(唯一)
- uri:请求转发的目的地
- 静态路由:http://localhost:8080/
- 动态路由:lb://服务名称
- predicates:那些请求需要使用该路由配置进行转发
- Path=/Result/** 依据请求路径来匹配转发的请求
- filter
- 可以使用官方定义的过滤器,但一般使用我们自己定义的
server:
port: 9527
spring:
application:
name: fs-gateway
cloud:
# 网关配置
gateway:
discovery:
locator:
enabled: true #使用情况少,不常用(了解) 开启从注册中心动态创建路由的功能,利用微服务进行路由,请求路径前可以添加微服务名称
lower-case-service-id: true #(了解) 允许请求路径的微服务名称为小写
# 路由配置:转发规则
routes:
- id: payment_routh #payment_routh 路由的id,若不配置,默认是UUID,没有固定规则但要求唯一
# uri: http://localhost:8001 # 静态路由 这样配置是死的,不好 匹配后提供服务的路由地址
uri: lb://FS-PROVIDER # 动态路由 匹配提供服务的路由地址,达到路由是去服务中心找端口,而不是写死:原理是使用自动集成Ribbon,使用默认配置
filters:
# 下面是官方给定的局部过滤器,我们一般还是使用自定义过滤器
# - AddRequestParameter=username,xiaofu # 通过过滤工厂会在匹配的请求头上加上一对请求头参数,名称为username值为xiaofu
predicates: # predicates就是为了实现一组匹配规则,让请求过来找到对应的route进行处理
- Path=/payment/get/** # 断言,路径相匹配的地址 注意Path要大写
# - Cookie=username,xiaofu #携带cookie访问,断言是否有,有就允许访问,这里写死了 使用cmd命令curl携带cookie测试 curl http://localhost:9527/payment/get/2 --cookie "username=xiaofu"
# 可以配置多个
# - id: payment_routh02 #payment_routh02 路由的id,没有固定规则但要求唯一
# uri: http://localhost:8001 #匹配后提供服务的路由地址
# predicates:
# - Path=/payment/lb/** # 断言,路径相匹配的地址
eureka:
instance:
hostname: fs-gateway-service
#服务提供者provider,注册进eureka服务列表内
client:
service-url:
register-with-eureka: true # 注册到服务中心
fetch-register: true # 从注册中心获取注册的服务信息
defaultZone: http://localhost:7001/eureka #,http://localhost2:7002/eureka,http://localhost3:7003/eureka # 注册中心地址
主启动
package com.fs.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
public static void main(String[] args) {
SpringApplication.run(GatewayMain9527.class,args);
}
}
自定义网关过滤器MyGatewayFilter(全局过滤器)
对请求进行功能的增强(认证、设置凭证信息等)
- 局部过滤器:只对配置的路由进行生效
- org.springframework.cloud.gateway.filter.factory.过滤器名称GatewayFilterFactory
- 全局过滤器:所有请求都生效
- GlobalFilter
- Ordered指定过滤器执行的顺序,越小越先执行)
package com.fs.springcloud.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
/*
自定义gateway路由全局过滤器,只需要编码,不需要进行任何配置
implements GlobalFilter, Ordered需要实现这两个接口
*/
@Component
@Slf4j
public class MyGatewayFilter implements GlobalFilter, Ordered {
//过滤方法
//测试http://localhost:9527/payment/get/2?username=xiaofu
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("~~~~~进到了MyGatewayFilter自定义过滤器:"+new Date().getTime());
//获取请求参数中有没有username
String username = exchange.getRequest().getQueryParams().getFirst("username");
//没有就非法
if (username== null){
log.info("~~~用户名为null,非法用户~~~~");
//没有传递用户名,就返回个状态码不能接受的状态码
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
//放行,向下继续执行
return chain.filter(exchange);
}
/**
* 过滤器排序
* @return 数值越低,越先加载
*/
@Override
public int getOrder() {
//加载过滤器顺序,数值越低,越先加载
return 0;
}
}