创作不易,各位看官点赞收藏.
Spring Cloud Alibaba 旨在为微服务开发提供一站式解决方案。该项目包括开发分布式应用程序和服务所需的组件,以便开发人员可以使用 Spring Cloud 编程模型轻松开发分布式应用程序。
使用Spring Cloud Alibaba,您只需添加一些注解和配置,您的应用程序就可以使用阿里巴巴的分布式解决方案,并通过阿里巴巴中间件构建您自己的分布式系统。
Spring Cloud 阿里巴巴的特点:
Nacos: 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
Nacos官网:https://nacos.io/zh-cn/index.html
安装方式有很多,可以去github上去下载对应的压缩包,但是下载慢。也可以去gitee上去找一个别人fork下源码,然后通过maven自己打包(要确定自己电脑上有Java环境和Maven环境)。
git clone https://github.com/alibaba/nacos.git
cd nacos/
# 打包命令
mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U
# 进入到这个文件下,会发现打包好的文件
cd distribution/target/nacos-server-$version/nacos/bin
Windows启动方式:
# 启动命令(standalone代表着单机模式运行,非集群模式):
startup.cmd -m standalone
注意:启动目录路劲不能包含中文,启动后就可以访问:127.0.0.1:8848/nacos/index.html
,出现下面的页面就是启动成功了。
Linux启动:
# Linux/Unix/Mac
sh startup.sh -m standalone
# ubuntu系统
bash startup.sh -m standalone
父spring-cloudalibaba的pom引入依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2021.0.4.0version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
本地引入nacos的pom依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
编写yaml文件:
server:
port: 8081
spring:
application:
name: pay-application-01 # 注册名称
cloud:
nacos:
server-addr: 127.0.0.1:8848 # 注册地址
management:
endpoints:
web:
exposure:
include: '*'
主启动类:
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现
public class PayApplication01 {
public static void main(String[] args) {
SpringApplication.run(PayApplication01.class,args);
}
}
启动服务并注册到Nacos中:
nacos可以进行服务的调用,也可以自己进行负载均衡。将服务消费者注册到nacos中,并使用RestTemplate
进行服务调用。
yaml文件编写:
server:
port: 80
# 服务消费者
spring:
application:
name: consumer-application # 注册名称
cloud:
nacos:
server-addr: 127.0.0.1:8848 # 注册地址
# 消费者将要访问的微服务名称
service-url:
nacos-pay-service-name: pay-application-01
由于Spring Cloud2020.0.1.0之后不再使用netflix,所以不使用Ribbon做负载均衡,采用下面方式进行服务调用。
我们使用LoadBalancerClient作为负载均衡,首先引入依赖。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-loadbalancerartifactId>
dependency>
@RestController
public class TestController {
@Resource
private RestTemplate restTemplate;
@Resource
private LoadBalancerClient loadBalancerClient;
@Value("${service-url.nacos-pay-service-name}")
private String SERVICE_NAME;
@GetMapping("/test")
public String test(){
ServiceInstance serviceInstance = loadBalancerClient.choose(SERVICE_NAME);
// 服务调用地址
String path = String.format("http://%s:%s/%s",serviceInstance.getHost(),serviceInstance.getPort(),"test");
System.out.println("request path:" +path);
// 通过RestTemplate调用服务
return restTemplate.getForObject(path,String.class);
}
}
每刷新一次,就会由不同服务提供者进行提供,这就实现了轮询的负载均衡。
Nacos可以作为一个配置中心实时更新项目中的配置文件,这样就可以只需修改一处配置文件使所有服务的配置文件都修改。Nacos在项目初始化时要先从配置中心拉取配置之后,才能保证项目的正常启动。
引入依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
编写配置文件:
一般有两种配置文件bootstrap.yaml
和application.yaml
,但是前者优先级高于后者,所以将全局的配置放在bootstrap中,自己配置文件放在application中。(写一个就可以了)
server:
port: 7001
spring:
profiles:
active: dev # 设置成开发环境
application:
name: config-application
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 服务注册地址
config:
server-addr: 127.0.0.1:8848 # 配置中心地址
file-extension: yml # 配置文件后缀名
编写好配置模块后,在Nacos中创建配置文件:
Data ID命名规则:
命名公式 ${spring.cloud.nacos.config.prefix}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
,注意每一个不能省略,中间使用 - 隔开。
可能新版的nacos不支持bootstrap文件,需要导入依赖。
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
以上的Data ID就该命名为:config-application-dev.yml
配置文件动态刷新:
@RestController
@RefreshScope // 配置文件动态刷新,当修改nacos中心的配置文件发布后,不用重启项目就会发现配置发生变化了
public class TestController {
@Value("${test.value}")
private String values;
@GetMapping("/test")
public String test(){
return values;
}
}
在分布式开发中存在多环境,通常在实际开发中都会准备不同的开发环境,例如dev开发环境、test测试环境、prod生产环境,如何进行管理这些环境配置保证每个服务读取到正确的配置文件,这就需要Nacos的分类配置。
Namespace、group、Data ID三者之间的关系:
Namespace区分部署环境,Group和Data ID逻辑上区分两个目标对象。相当于Java中的包名、类名。默认的Namespace是public,Group是DEFAULT_GROUP。
Nacos三种分配配置方式:(开发环境、测试环境、生产环境)
# 项目切换不同环境
spring:
profiles:
active: dev # 设置成开发环境,对应配置中心Data ID为config-application-dev.yml
# active: test # 设置成测试环境,对应配置中心Data ID为config-application-test.yml
# active: prod # 设置成生产环境,对应配置中心Data ID为config-application-prod.yml
配置中心创建对应的配置文件,是在默认的public的Namespace下,默认的DEFAULT_GROUP分组下,分别切换对应环境就可以实现不能拉取不同环境的配置文件。
创建两个相同Data ID的配置文件,但是在不同的分组下。
spring:
profiles:
active: dev # 设置成开发环境,对应配置中心Data ID为config-application-dev.yml
# active: test # 设置成测试环境,对应配置中心Data ID为config-application-test.yml
# active: prod # 设置成生产环境,对应配置中心Data ID为config-application-prod.yml
application:
name: config-application
cloud:
refresh:
enabled: true
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 服务注册地址
config:
server-addr: 127.0.0.1:8848 # 配置中心地址
file-extension: yml # 配置文件后缀名
group: DEV_GROUP # 切换对应分组
创建两个不同的命名空间,并创建两个GROUP和Data ID相同的配置文件,默认有一个public命名空间不能够删除。
在不同分组中创建了两个相同GROPU和Data ID的配置文件,并修改配置文件来切换不同的命名空间。
spring:
profiles:
active: dev # 设置成开发环境,对应配置中心Data ID为config-application-dev.yml
# active: test # 设置成测试环境,对应配置中心Data ID为config-application-test.yml
# active: prod # 设置成生产环境,对应配置中心Data ID为config-application-prod.yml
application:
name: config-application
cloud:
refresh:
enabled: true
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 服务注册地址
config:
server-addr: 127.0.0.1:8848 # 配置中心地址
file-extension: yml # 配置文件后缀名
group: TEST_GROUP # 切换对应分组
namespace: module-dev # 切换不同的Namespace,填创建时的命名空间ID
在0.7版本之前,在单机模式时nacos使用嵌入式数据库(derby)实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力。对于搭建Nacos集群数据库为了保证数据的一致性,也是使用MySQL数据库来存放数据保证数据高可用。
初始化Nacos数据库脚本:在Nacos的conf目录下有一个nacos-mysql.sql脚本,复制后在MySQL来执行。
修改Nacos的数据源:修改conf目录下的
application.properties
文件,在文件最下面添加下面一句话。
# 一定要是mysql
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=1234567
重启Nacos并插入数据观察数据库是否增加数据记录,如果增加数据库就切换成功,这样新增的数据存放在数据库中,重启Nacos数据就不会丢失。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tvtSrau-1692611041818)(https://jx-image-storage.oss-cn-hangzhou.aliyuncs.com/image/image-20221102112013186.png)]
在生产环境中Nacos都已集群的形式存在,这样保证了服务高可用。一般集群模式是客户端请求到代理服务器,然后代理服务器转发到每一台Nacos上。下面介绍使用1台Nginx+3台Nacos+1台MySQL做Nacos集群。
安装编译环境:
# g++环境
yum -y install gcc automake autoconf libtool make
yum install gcc gcc-c++
# 安装pcre、zlib、openssl
yum install pcre -y
yum install pcre-devel -y
yum install zlib -y
yum install zlib-devel -y
yum install openssl -y
yum install openssl-devel -y
下载tar包,并解压,默认解压后的路径是
/usr/local/nginx
#解压
cd /usr/local
tar -zxvf nginx-1.16.0.tar.gz
#进行configure配置,查看是否报错
cd nginx-1.16.0/
./configure
#编译
make
#安装
make install
#在 /usr/local/nginx目录下,可以看到如下4个目录:
#conf配置文件,html网页文件,logs日志文件,sbin主要二进制程序
修改Nginx配置文件并启动:
#指定配置文件启动
/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
# 使用内置数据源
sh startup.sh -p embedded
# 使用外置数据源,我们使用这个
sh startup.sh
由于Nacos集群服务器启动需要很大内存,一般普通的云服务跑不起来,可以修改启动命令,将初始化的内存值改小。
避坑端口偏移:Nacos 2.x 新增了 gRPC 协议的通讯端口,在启动时会自动在原有端口 port 的基础上根据偏移量 1000 和 1001 再打开另外两个端口。所以在一台服务器上去跑两个nacos不要使用相邻的端口号和不要使用偏移端口号,同时打开这两个端口。
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
下载地址:https://github.com/alibaba/Sentinel/releases/tag/v1.8.0
java -Dsentinel.dashboard.auth.username=admin -Dsentinel.dashboard.auth.password=admin123 -jar sentinel-dashboard-1.8.1.jar &
启动:下载完成后是一个Jar包,直接通过
java -Dserver.port=7777 -jar sentinel-dashboard-1.8.0.jar
命令启动后就可以直接访问sentinel的控制面板,账号和密码都是sentinel,不指定端口就是默认8080端口。在Centos中运行jar包:
nohup java -Dserver.port=7777 -jar sentinel-dashboard-1.8.0.jar >sentinel.log 2>&1 &
nohup意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行
>spring.log代表将命令的输出定向存储到spring.log这个文件中,文件名可以自己定义。
&代表在后台运行
导入依赖:
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
修改yaml,使用sentinel监控服务:
server:
port: 8081
spring:
profiles:
active: dev
application:
name: sentinel-application-01
cloud:
nacos:
discovery:
server-addr: xxxxxxx:8001
config:
server-addr: xxxxxxxx:8001 # 配置中心地址
file-extension: yml # 配置文件后缀名
sentinel:
transport:
# sentinel的地址
dashboard: 127.0.0.1:8080
# 默认端口号,如果被占用自动+1扫描,直到未被占用端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
启动服务,需要请求服务服务的某个接口,不然sentinel是空白页面。
簇点链路:访问服务对应的访问路径,也会监控QPS、线程数等数据
流控规则:对某个簇点设置对应的流控规则。
阈值 = 阈值\3
之前开始预热,会设置一个预热时间,在这个时间阈值有一个缓冲上升直到达到设置阈值。QPS:指每一秒中请求api的次数。
线程数:
指服务中处理请求的线程数。
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态(调用超时或异常比例升高),会对这个资源的请求进行限制,让请求快速失败,以避免影响到其它资源导致联错误。当服务降级后,在降级时间窗口之内,调用该资源就会自动熔断抛出异常(DegradeException)。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
编写一个热点限流接口:
@GetMapping("/hot")
// 作为热点限流的唯一标识,一般写rest接口名
// blockHandler:指定一个限流后的处理方法
@SentinelResource(value = "hot",blockHandler = "paramFlowExceptionHandler")
public String hotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2){
return "success test";
}
public static String paramFlowExceptionHandler(String p1, String p2, BlockException blockException){
return p1+"热点数据被限流";
}
配置热点参数限流:
上面就表示,当访问的接口为url为:127.0.0.1:8081/hot?p1=xxx
时,只要p1这个参数不为空,并且QPS超过阈值就会进行热点限流。如果p1为空就没有限流规则。参数索引就是指定需要进行热点参数的下标位置,从0开始。
参数例外项:当有时候我们希望热点参数为某个值时,对应的QPS的限流不一样,可以通过参数例外项进行控制。
Sentinel 系统自适应保护从整体维度对应用入口流量进行控制,结合应用的 Load、总体平均 RT、入口 QPS 和线程数等几个维度的监控指标,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量,比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
maxQps * minRt
计算得出。设定参考值一般是 CPU cores * 2.5
。@SentinelResource
注解基于资源名的流控、降级、热点:将@SentinelResource的value属性作为资源名,之前使用的rest地址作为资源名。
@GetMapping("/resource/test1")
@SentinelResource(value = "test1",blockHandler = "handlerTest1")
public String test(){
return "test1";
}
public static String handlerTest1(BlockException e){
return "处理test1的sentinel异常";
}
blockHandler属性:
用于处理当满足在sentinel设置的规则进行处理时,自定义处理方法名,设置后不再使用sentine默认的返回值。
若希望使用其他类的函数,则可以指定 blockHandlerClass
为对应的类的 Class
对象,注意对应的函数必需为 static 函数,否则无法解析。但是使用上面方式去处理会发现每一个接口都需要一个自定义处理方法,这样代码膨胀,需要全局去处理。
blockHandler和fallback的理解:
blockHandler:指定一个方法去处理违反了sentinel配置规则的请求,不在使用sentinel默认的处理方式。
fallback:指定一个方法去处理在请求接口时出现了Java异常时,进行异常处理,不在用户页面展示报错信息。
@GetMapping("/resource/test2")
// fallback:指定处理异常的方法
@SentinelResource(value = "test2",fallback = "fallbackHandler")
public String test1(){
int i = 1/0;
return "test1";
}
// 方法名指定、static修饰、返回值类型相同、参数相同并且加一个Throwable、public修饰
public static String fallbackHandler(Throwable e){
return "处理test1的Java异常"+e.getMessage();
}
注意 blockHandler只处理在违背了sentinel配置错误时的异常,fallback只处理Java运行时的异常。
exceptionsToIgnore属性:参数是数组,当出现数组里面的异常时不进行处理
Sentinel规则持久化:默认在Sentinel的规则都是临时的,只要关闭了服务所有的规则就会消失。下面介绍将Sentinel规则持久化进Nacos中。
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
Feign是一个声明式 Web 服务客户端。 它使编写 Web 服务客户端变得更加容易。 要使用Feign创建一个接口并对其进行注释。 它具有可插拔的注释支持,包括Feign注释和JAX-RS注释。 Feign还支持可插拔编码器和解码器。 Spring Cloud 增加了对 Spring MVC 注释的支持,并支持在 Spring Web 中默认使用相同注释。 Spring Cloud 集成了 Eureka 和 Spring Cloud LoadBalancer,以便在使用 Feign 时提供负载平衡的 http 客户端。HttpMessageConverters
导入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
开启openfeign:使用
@EnableFeignClients
注解开启,basePackages
数组属性:去指明应用程序A在启动的时候需要扫描服务B中的标注了@FeignClient注解的接口的包路径。
@SpringBootApplication
// 扫描com.cj包下所有带有@FeignClient的接口
@EnableFeignClients(basePackages = {"com.cj"})
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
编写接口:
// value:是调用哪个注册到nacos中心服务的服务名称
// contextId:这个是调用注入时是bean的名称
@FeignClient(value = SourceConstants.ORDER_SOURCE)
public interface OrderRemoteService {
// 在这个服务下对应的调用的那个接口
@GetMapping("/order")
R order();
}
编写服务调用:
@RestController
public class ConsumerController {
// 注入编写好的openfeign接口,然后直接可以进行调用方法
@Resource
private OrderRemoteService orderRemoteService;
@GetMapping("/1")
public R consumer(){
return orderRemoteService.order();
}
}
总结:
在时机开发中,一些业务的处理时间可能和Openfeign默认的超时时间存在时间差,导致业务是正常时间完成但是Openfeign会报TimeOutException,所以需要我们自己去控制Openfeign的超时时间。
模拟超时异常:在业务逻辑让线程睡5s
@GetMapping("/timeOut")
public R timeOut(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return R.success("端口获取成功",port);
}
远程调用:
@GetMapping("/2")
public R timeOut(){
return orderRemoteService.timeOut();
}
修改超时时间:
配置日志:编写一个配置类,设置对应的日志等级。
@Configuration
public class OpenfeignConfig {
@Bean
Logger.Level openFeignLoggerLevel(){
return Logger.Level.FULL;
}
}
配置日志:
logging:
level:
# 配置openfeign以什么级别去监控哪个接口
com.jx.*: debug
Openfeign+Sentinel远程服务异常处理:
feign:
sentinel:
enabled: true
client:
config:
default:
# 修改openfeign的超时时间
readTimeout: 5000 # 修改为5s
connectTimeout: 5000
// 当调用远程服务出异常时,通过这个类进行处理,实现FallbackFactory接口,并重写create方法
@Component
public class OrderRemoteFallbackFactory implements FallbackFactory<OrderRemoteService> {
private static final Logger log = LoggerFactory.getLogger(OrderRemoteFallbackFactory.class);
@Override
public OrderRemoteService create(Throwable cause) {
log.error("order远程服务调用失败:{}", cause.getMessage());
return new OrderRemoteService() {
@Override
public R order() {
// 在这个方法中返回出现异常后的远程调用结果
return R.error("order远程服务调用失败:{}",cause.getMessage());
}
@Override
public R timeOut() {
return R.error("order远程服务调用失败:{}",cause.getMessage());
}
@Override
public R exceptionTest() {
System.out.println("============================================");
return R.error("order远程服务调用失败:{}",cause.getMessage());
}
};
}
}
@FeignClient
注解上添加fallbackFactory
属性@FeignClient(contextId = "orderRemoteService",value = SourceConstants.ORDER_SOURCE,fallbackFactory = OrderRemoteFallbackFactory.class)
public interface OrderRemoteService {
@GetMapping("/order")
R order();
@GetMapping("/timeOut")
R timeOut();
@GetMapping("/exception")
R exceptionTest();
}
/exception
@GetMapping("/exception")
public R exceptionTest() throws Exception {
R r = null;
r.put("name","name");
return r;
}
服务网关:在微服务的架构中,所有请求需要先通过网关,再由网关将请求路由转发到对应的每一个服务上。
Spring Cloud Gateway 使用的是Webflux中的reactor-netty响应式编程组件,底层是一个Netty通讯框架。
Webflux是一个异步非阻塞框架,那么Gateway也是一个异步非阻塞模型,对于高并发请求性能很好。它能与 SpringCloud 生态很好兼容,单从流式编程+支持异步上也足以让开发者选择它了
Gateway的三大组件:
引入网关依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
exclusion>
exclusions>
dependency>
注意,需要去除掉spring-boot-starter-web
,不然启动会报错。
路由配置:Route 主要由 路由id、目标uri、断言集合和过滤器集合组成
spring:
profiles:
active: dev
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8001
config:
server-addr: 127.0.0.1:8001
file-extension: yml
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
routes: # 配置路由
- id: order # id,唯一标识
uri: http://127.0.0.1:8000 # 路由转发的目标地址
# 如果访问地址为 http://127.0.0.1:9000/order/test,他就会路由断言成功,就会转发到http://127.0.0.1:8000/test(去除了一级访问地址)
predicates: # 断言判断,如果满足就进行路由转发
- Path=/order/**
filters:
- StripPrefix=1 # 原始路去除到一级,上面就去除到/order
- id: pay
uri: http://127.0.0.1:9011
predicates:
- Path=/pay/**
filters:
- StripPrefix=1
Gateway动态路由配置:在服务架构中,服务可能是部署到多台服务上的,直接配置服务的url地址不方便,一旦服务ip地址发生变化就需要改配置,所以需要在注册中心去通过服务名称去发现服务并实现负载均衡的路由转发。
gateway:
discovery:
locator:
# 路由的路径默认会使用大写ID,若想要使用小写ID,可将lowerCaseServiceId设置为true
lowerCaseServiceId: true
# 开启从注册中心动态创建路由功能
enabled: true
routes: # 动态配置路由
- id: order
uri: lb://cj-order # 固定格式:lb://+服务名
predicates:
- Path=/order/**
filters:
- StripPrefix=1
- id: pay
uri: lb://modules-pay-01
predicates:
- Path=/pay/**
filters:
- StripPrefix=1
loadbalancer
。<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
- id: pay
uri: lb://modules-pay-01
predicates:
# 表示这个路径需要在这个时间之后访问才有效
- After=2022-11-08T17:22:39.520+08:00[Asia/Shanghai]
# 在这个时间之前访问才有效
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
# 在这个时间时间段访问才有效
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2022-11-08T17:22:39.520+08:00[Asia/Shanghai]
# 带有指定cookie名的请求,并且值是指定的(支持正则)
- Cookie=chocolate, ch.p
# 带有指定的Header的请求,并且值是指定的(支持正则)
- Header=X-Request-Id, \d+
# Host带有指定值的,且值是指定的(支持正则)
- Host=**.somehost.org,**.anotherhost.org
# 指定方法的请求才有效果,可以指定多个,用逗号隔开
- Method=GET,POST
# 匹配请求路径
- Path=/pay/**
# 必须参数和正则参数,必须参数请求必须带有指定参数值的参数,正则参数就是满足对应正则的参数即可
- Query=green, gree.
# 请求的远程地址符合指定地址
- RemoteAddr=192.168.1.1/24
# 同一个uri的通过group分组,值表示权重,权重越高在负载均衡的时候分配的流量越大
- Weight=group1, 2
时间格式的获取:
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
// 2022-11-08T16:44:48.004+08:00[Asia/Shanghai]
}
Gateway自己的过滤器分为局部过滤和全局过滤,每个过滤器都有pre和post,局部有34个全局有9个。
文档地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters]
自定义局部过滤器:可以自己定义局部过滤器让对应过滤器在对应的路由上使用。
// 配置局部过滤器,需要继承AbstractGatewayFilterFactory,可能后续版本有用
@Component
@Slf4j
public class PartFilter extends AbstractGatewayFilterFactory<Object> {
// 进行过滤操作
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) ->{
// 获取请求
ServerHttpRequest request = exchange.getRequest();
List<String> usernames = request.getQueryParams().get("username");
System.out.println(usernames.toString());
if (usernames==null || usernames.size()==0 || !usernames.get(0).equals("张三")){
ServerHttpResponse response = exchange.getResponse();
log.error("非法用户");
// 直接拦截请求并返回错误信息
return response.setComplete();
}
log.info("合法用户");
// 放行请求
return chain.filter(exchange);
};
}
}
filters:
# 将局部filter设置到对应的路由中,过滤器的名称
- PartFilter
- StripPrefix=1
配置全局过滤器:全局过滤器回去过滤网关中所有的路由,直接注入就可使用,不用其它配置。
@Component
@Slf4j
public class FilterTest implements GlobalFilter, Ordered {
// 自定义全局过滤操作
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求
ServerHttpRequest request = exchange.getRequest();
List<String> usernames = request.getQueryParams().get("username");
System.out.println(usernames.toString());
if (usernames==null || usernames.size()==0 || !usernames.get(0).equals("张三")){
ServerHttpResponse response = exchange.getResponse();
log.error("非法用户");
// 直接拦截请求并返回错误信息
return response.setComplete();
}
log.info("合法用户");
// 放行请求
return chain.filter(exchange);
}
// 设置过滤器的启动优先级,数字越小越先启动
@Override
public int getOrder() {
return 0;
}
}