SpringCloud-Alibaba GitHub官方地址:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
SpringCloud-Alibaba 官方文档:
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
要用springCloud-Alibaba 首先需要引入alibaba提供的 dependencyManagement 控制版本
<dependencyManagement>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>2020.0.0version>
<type>pomtype>
<scope>importscope>
dependency>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2021.1version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
springBoot,springCloud和springCloud Alibaba之间的版本依赖关系:
https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明
Spring Cloud Version | Spring Cloud Alibaba Version | Spring Boot Version |
---|---|---|
Spring Cloud 2020.0.0 | 2021.1 | 2.4.2 |
Spring Cloud Hoxton.SR9 | 2.2.6.RELEASE | 2.3.2.RELEASE |
Spring Cloud Greenwich.SR6 | 2.1.4.RELEASE | 2.1.13.RELEASE |
Spring Cloud Hoxton.SR3 | 2.2.1.RELEASE | 2.2.5.RELEASE |
Spring Cloud Hoxton.RELEASE | 2.2.0.RELEASE | 2.2.X.RELEASE |
Spring Cloud Greenwich | 2.1.2.RELEASE | 2.1.X.RELEASE |
Spring Cloud Finchley | 2.0.4.RELEASE(停止维护,建议升级) | 2.0.X.RELEASE |
Spring Cloud Edgware | 1.5.1.RELEASE(停止维护,建议升级) | 1.5.X.RELEASE |
Nacos官方文档:https://nacos.io/zh-cn/docs/what-is-nacos.html
1 导入依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
2 启动器上加**@EnableDiscoveryClient** 注解
3 yaml配置
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
discovery:
#是否为临时节点,如果否则断线后不会删除,默认是临时节点(断线后自动删除)
ephemeral: false
导入依赖
测试发现springCloud 2021.1以后的版本需要额外引入spring-cloud-starter-bootstrap
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-bootstrapartifactId>
dependency>
创建bootstrap.yml
spring:
application:
name: employee
cloud:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
config:
file-extension: yaml
启动项目后会自动去nacos中拉取 employee.yaml的配置文件
拉取文件的规则是 s p r i n g . a p p l i c a t i o n . n a m e − {spring.application.name}- spring.application.name−{spring.profiled.active}.${file.extension}
在项目中我们可能会将公共的配置抽取出来放在单独的配置文件中(如果common.yaml),nacos配置中心也能够通过配置读取这些额外的配置文件
shared-configs 和 extensionConfigs 的功能完全一样 不同的是 extensionConfigs 优先级高于 shared-configs:
默认的(emp-dev.yaml ) > extensionConfigs > shared-configs
spring:
cloud:
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
config:
file-extension: yaml
shared-configs:
#优先级最低
- dataId: common.properties
refresh: true #如果设置为false这不会动态刷新
extensionConfigs:
- dataId: common2.properties
refresh: true
通常我们会通过@Value引用配置文件的配置内容,但是没有动态刷新的功能,如果要动态刷新,得在类上加==@RefreshScope注解实现动态刷新==
nacos 内嵌了数据库实现持久化机制,在0.7版本后还支持了外部的mysql数据库(mysql版本必须为5.6.5+)
配置msyql持久化
1:初始化 conf/nacos-mysql.sql 文件
2:在conf/application.properties 文件内配置 数据库连接,用户名和密码
spring.datasource.platform=mysql
#数据库数量 单机为1个
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
db.user=root
db.password=root
注: nacos内部使用的数据库驱动的5版本的,因此不支持Mysql8版本,如果你的Mysql版本为8则需要替换数据库驱动:
在nacos根目录创建 plugins/mysql 文件夹 并放入新的mysql驱动jar包,application.properties内的数据库连接也要换成Mysql8的连接
这样nacos就会持久化到Mysql数据库中
配置nacos服务器的配置文件
nacos.core.auth.enabled=true
SpringCloud新版本中弃用了Ribbon,由LoadBalancer替代
1 引入依赖:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
2 添加@LoadBalanced
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
方法一:直接通过服务名调用
//直接通过 服务名调用
@Autowired
private RestTemplate restTemplate;
@GetMapping("/get")
public String getDept(){ //deptApp代表了服务名
return restTemplate.getForObject("http://deptApp/hello",String.class);
}
方法二: 通过LoadBalancerClient 实现负载均衡
//通过LoadBalancerClient 实现负载均衡
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient balancerClient;
@GetMapping("/get")
public String getDept(){ //获取Dept服务的信息
ServiceInstance dept = balancerClient.choose("Dept");
//获取ip和端口号 此时会调用负载均衡算法动态获取
String url = String.format("http://%s:%s/%s",dept.getHost(),dept.getPort(),"hello");
return restTemplate.getForObject(url,String.class);//调用
}
OpenFeign底层整合了LoadBalancer,以接口的形式调用微服务
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
使用:
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class EmpApplication {
public static void main(String[] args) {
SpringApplication.run( EmpApplication.class,args);
}
}
//value指定要调用的微服务名
@FeignClient(value = "dept")
public interface DeptFeign {
@GetMapping("/hello")
public String hello();
}
@Autowired
private DeptFeign deptFeign;
@GetMapping("/hello")
public String hello(){
return deptFeign.hello();
}
全局配置
@Configuration
public class DeptFeignConfig implements RequestInterceptor{
//feign在发送请求的时候回调用此方法,允许我们对请求进行操作
@Override
public void apply(RequestTemplate requestTemplate) {
MethodMetadata methodMetadata = requestTemplate.methodMetadata();
//获取请求方式
Method method = methodMetadata.method();
requestTemplate.query("token","abc");
requestTemplate.header("token","123");
//获取请求路径
System.out.println("---->"+requestTemplate.request().url());
}
//开启feign日志
//NONE:不输出日志
//BASIC:输出请求方法、URL、响应状态码、执行时间
//HEADERS:BASIC+及请求和响应头
//FULL:请求和响应的heads、body、metadata
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
//设置调用超时时间
@Bean
public Request.Options feignOptions(){
return new Request.Options(1, TimeUnit.SECONDS,2,TimeUnit.SECONDS,true);
}
}
feign日志的日志级别为debug,但是SpringBoot的默认日志级别为InFo
需要配置才能打印出feign日志
#springboot 默认的日志级别是info,改为debug
logging:
level: #指定这个包下的日子为debug
org.gjw.feign.*: debug
#feign的日志级别还可以这样配置
feign:
client:
config:
dept:
#配置 调用 dept微服务超时时间
connectTimeout: 100 #连接时间
readTimeout: 1000 #执行时间
Feign支持对请求与响应进行GZIP压缩,以减少通信中的性能损耗,默认对请求和相应压缩是禁用的
如果要启用需要配置:
开启压缩可以有效节约网络资源,但是会增加CPU压力,建议把最小压缩的文档大小适度调大一点
feign:
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 4096
response:
enabled: true
useGzipDecoder: true
如果要为每个微服务单独配置,则可以通过@FeignClient指定配置类
//使用 DeptFeignConfig 内的配置
@FeignClient(value = "dept",configuration = DeptFeignConfig.class)
public interface DeptFeign {
@GetMapping("/hello")
public String hello();
}
Sentinel官网: https://sentinelguard.io/zh-cn/
1 导入依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
2 配置 yaml
sentinel:
transport:
dashboard: 127.0.0.1:8080 #配置Dashboard的ip和的客户
port: 8719 # #配置与Dashboard交互的端口 随便定义一个即可(只要未被占用,如果被占用会自行+1寻找端口)
指处理请求的最大线程数,一旦线程数超过指定阈值直接流控
Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目 (正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端 进行配置。
流控模式的直接模式: 当访问的资源被流控后直接返回错误信息.
流控模式的联模式: 当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写 操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本 身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 流控模式为关联 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。
对一条链路的入口资源进行限流:
注意必须在application.yaml配置sentinel关闭接口收敛模式:
当QPS超过任意规则的阈值后,新的请求就会被 立即拒绝,拒绝方式为抛出FlowException
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量 突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐 增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。 冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值,当接口一段时间不被访问又会进入冷启动状态,重新预热.
冷加载因子可以在客户端配置文件配置
spring.cloud.sentinel.flow.cold-factor: 3
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式会严格控制请求通过的间隔时间,也即是让请 求以均匀的速度通过,对应的是漏桶算法。
排队等待主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下 来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的 请求。
@SentinelResource
-Dcsp.sentinel.statistic.max.rt=9999 设置 降级规则内的RT(基于响应时间的降级)最大值
授权规则: 需要实现RequestOriginParser
sentinle自定义被限流或熔断时抛出的异常
2.2.0一下版本 需要实现UrlBlockHandler接口
2.2.0以上 实现 BlockExceptionHandler接口
public class MyExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
// FlowException 限流一次
//ParamFlowException 参数限流异常
//DegradeException 降级异常
//AuthorityException 授权异常(黑白名单)
//SystemBlockException 系统异常
if(e instanceof FlowException){
System.out.println("限流异常");
}else if(e instanceof DegradeException){
System.out.println("降级异常");
}
}
}
慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间), 请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并 且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态 (HALFOPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会 再次被熔断。
以上配置表示:对/read_db接口进行统计,时间为5秒,5秒内如果接口处理时间超过100毫秒的比例为10%则对接口进行熔断.
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例 大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALFOPEN 状 态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0], 代表 0% 100%。
该配置表示 /read_db 在1秒内发送异常的比例超过0.5后进行熔断,熔断时间为5秒
异常数 (ERROR_COUNT):当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探 测恢复状态(HALFOPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。
注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效
以上配置表示: /read_db在5秒内发送异常此时超过1后(必须5次请求)进行熔断,时间为5秒
seata支持3种存储模式:
修改模式为db:
打开config/file.conf
,修改mode=db,然后修改db配置项内的信息,然后创建数据库并在数据库创建相应的表
该sql脚本位于seata源码下script\server\db\mysql.sql
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(96),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
配置Nacos注册中心: 负责事务参与者(微服务) 和TC通信
将Seata Server注册到Nacos,修改conf目录下的registry.conf,将type改为nacos,然后修改nacos的配置
配置Nacos配置中心
如果配置了seata server使用nacos作为配置中心,则配置信息会从nacos读取,file.conf可以不用配置。 只用配置registry.conf 使用nacos时也要注意group要和seata server中的group一致,默认group是"DEFAULT_GROUP"
1 修改 /seata/script/config-center/config.txt修改配置信息
config.txt位于seata的源码,必须下载源码才能操作
transport.type=TCP
transport.server=NIO
#注意guagnzou(事务分组名称)可以自定义,default要和register.conf内的nacos.cluster一致
service.vgroupMapping.guangzou=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#注意修改这里为db
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://localhost:3306/seata?useUnicode=true
store.db.user=root
store.db.password=root
2.执行nacos-config.sh脚本在nacos内生成配置文件
nacos-config.sh位于nacos的源码 script\config-center\nacos下
以上步骤配置完成后即可执行bin/seata-server.bat 启动seata了
参数 | 全写 | 作用 | 备注 |
---|---|---|---|
-h | –host | 指定注册到注册中心的ip | 不指定获取当前ip,外网访问建议指定 |
-p | –port | 启动时的端口 | 默认8091 |
-m | –storeMode | 日志存储方式 | 支持file,db,redis默认为file |
-n | –serverNode | 指定当前seata-server节点id | 分布式使用,全局唯一默认1 |
-e | –seataEnv | 指定seata-server运行环境 | 如 dev,test 服务启动时会加载 registry-dev对应的配置 |
在微服务中引入依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-seataartifactId>
dependency>
spring:
cloud:
alibaba:
seata:
tx-service-group: guangzou #事务分组,要和config.txt内的分组保持一致
seata:
registry:
type: nacos #设置注册中心模式
nacos:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
application: seata-server
group: SEATA_GROUP #group要和register.conf保持一致
config:
type: nacos #配置中心配置
nacos:
server-addr: ${spring.cloud.nacos.server-addr}
username: nacos
password: nacos
group: SEATA_GROUP #group要和register.conf保持一致
该sql脚本位于seata源码下的 script\client\at\db\mysql.sql
必须在所有操作到的数据库内创建!
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
使用@GlobalTransactional 开启事务
远程服务对应的service方法也要加上
@GlobalTransactional
@Override
public boolean buy(String name) {
boolean flag = orderService.genOrder(name);
int i=1/0;
return flag ? baseMapper.minus(name) : flag;
}
spring Cloud Gateway 依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webfluxartifactId>
dependency>
配置Predicates断言可以对请求进行拦截判断,只有断言通过才允许访问
Gatewary内置的断言:
Before:在指定时间前允许访问
After:在指定时间后允许访问
Between:在指定时间范围内允许访问
配置的时间格式必须为 ZonedDateTime 格式 (可以用 ZonedDateTime.now()查看格式 )
predicates:
- Path=/hello/**
- Before=2020-04-13T21:27:04.242+08:00[Asia/Shanghai]
- After=2020-04-13T21:27:04.242+08:00[Asia/Shanghai]
- Between=2020-04-13T21:27:04.242+08:00[Asia/Shanghai],2020-04-13T21:27:04.242+08:00[Asia/Shanghai]
spring:
cloud:
gateway:
routes:
# 匹配指定cookie的路由
- id: cookie_route
uri: http://www.taobao.com
predicates:
- Cookie=chocolate, abc #携带了chocolate并且值为正则表达式匹配的值(这是是abc)
spring:
cloud:
gateway:
routes:
# 匹配指定请求头的路由
- id: header_route
uri: http://www.jd.com/
predicates: #有X-Request-Id请求头并且值为数字的
- Header=X-Request-Id, \d+
spring:
cloud:
gateway:
routes:
# 匹配指定路径的路由
- id: path_route
uri: http://www.baidu.com
predicates:
- Path=/baidu/** #访问/baidu/转换到 http://www.baidu.com/baidu/**
spring:
cloud:
gateway:
routes:
# 匹配指定请求参数的路由
- id: cookie_route
uri: http://tmall.com/
predicates: #请求参数要有param1 并且为数字
- Query=param1,\d+
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates: #必须为get请求
- Method=GET
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://example.org
predicates: #地址必须匹配
- RemoteAddr=192.168.1.1/24
gateway允许我们自定义断言工厂去创建我们自定义的断言,只需要实现AbstractRoutePredicateFactory即可
自定义类的名字必须以RoutePredicateFactory结尾
参照QueryRoutePredicateFactory创建我们自己的断言:
@Component
public class NameRoutePredicateFactory extends AbstractRoutePredicateFactory<NameRoutePredicateFactory.Config> {
public NameRoutePredicateFactory() {
super(NameRoutePredicateFactory.Config.class);
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("name");
}
@Override
public Predicate<ServerWebExchange> apply(NameRoutePredicateFactory.Config config) {
return (GatewayPredicate) exchange -> {
ServerHttpRequest request = exchange.getRequest();
List<String> nameList = request.getQueryParams().get("name");
//request参数内必须包含
return CollUtil.isNotEmpty(nameList) && nameList.contains(config.getName());
};
}
@Validated
public static class Config {
@NotEmpty
private String name;
public Config() {
}
public String getName() {
return this.name;
}
public Config setName(String name) {
this.name = name;
return this;
}
}
}
使用:
spring:
cloud:
gateway:
routes:
- id: emp
uri: lb://emp
predicates:
- Path=/emp/**
- Name=zs #使用我们自己的断言
filters:
- StripPrefix=1
Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。
GatewayFilter :应用到单个路由或者一个分组的路由上。
GlobalFilter :应用到所有的路由上。
局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在
Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。这里简单将
Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使。
过滤器工厂 | 作用 | 参数 |
---|---|---|
AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand 的名称 |
FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
RemoveHopByHopHeadersFilter | 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
SaveSession | 在转发请求之前,强制执行WebSession::save 操作 |
无 |
secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
SetPath | 修改原始的请求路径 | 修改后的路径 |
SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large |
请求包大小,单位为字节,默认值为5M |
ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 |
使用:
通过 StripPrefix 实现 访问 /emp/a 会转发到emp访问的/a接口
spring:
cloud:
gateway:
routes:
- id: emp
uri: lb://emp
predicates:
- Path=/emp/**
- Name=zs
filters:
- StripPrefix=1
实现自定义的局部过滤器和 自定义断言一样,类名必须为GatewayFilterFactory后缀,然后实现AbstractGatewayFilterFactory,可以参照内置的过滤器实现自定义的过滤器
全局过滤器对所有请求有效,不需要再配置文件进行配置。springCloud Gateway内置了很多全局过滤器:
通过实现GlobalFilter 和 ordered 接口可以对请求在网关进行拦截 判断请求
@Component
@Slf4j
public class MyFilter implements GlobalFilter, Ordered {
@Override //重写 GlobalFilter 接口方法
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("========***MyFilter**========");
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> queryParams = request.getQueryParams();
String name = queryParams.getFirst("name");
log.info("data( name )==>"+name);
if(name == null){ //没有用户名 拦截返回错误
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
} //放行
return chain.filter(exchange);
}
@Override //重写 ordered的方法 返回本filter的加载顺序
public int getOrder() {
return 0;
}
}
要启动gateway的访问日志,需要设置 -Dreactor.netty.http.server.accessLogEnabled=true
这个配置是java属性,不能通过springboot配置
方式一: 通过代码配置
@Configuration
public class GwCorsFilter {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // 允许cookies跨域
config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
config.addAllowedMethod("OPTIONS");// 允许提交请求的方法类型,*表示全部允许
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source =
new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
方式二: 通过yaml配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
1 引入依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-sentinel-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
2 配置配置文件
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
skywalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器。skywalking是分布式系统的应用 程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。它是一款优秀的 APM(Application Performance Management)工具,包括了分布式追踪、性能指标分析、应用和服务依赖分析等。
官网:http://skywalking.apache.org/
下载:http://skywalking.apache.org/downloads/
Github:https://github.com/apache/skywalking
文档: https://skywalking.apache.org/docs/main/v8.4.0/readme
中文文档: https://skyapm.github.io/document-cn-translation-of-skywalking
下载: https://skywalking.apache.org/downloads/
下载好后直接进入bin目录点击start.bat即可启动,启动成功后通过浏览器访问808端口即可看到skywalking页面
如果启动时出错,能是前端项目的8080端口被占用,可以修改webapp内的webapp.yml的端口配置
skysalking默认使用h2(内存数据库)进行数据存储,重启后日志会失效,我们可以换位mysql,Elasticsearch等数据库存储数据
使用mysql:
storage:
selector: ${SW_STORAGE:mysql} #默认是h2,修改为mysql
mysql:
properties:
jdbcUrl: ${SW_JDBC_URL:"jdbc:mysql://localhost:3306/swtest"}
dataSource.user: ${SW_DATA_SOURCE_USER:root}
dataSource.password: ${SW_DATA_SOURCE_PASSWORD:root}
dataSource.cachePrepStmts: ${SW_DATA_SOURCE_CACHE_PREP_STMTS:true}
dataSource.prepStmtCacheSize: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_SIZE:250}
dataSource.prepStmtCacheSqlLimit: ${SW_DATA_SOURCE_PREP_STMT_CACHE_SQL_LIMIT:2048}
dataSource.useServerPrepStmts: ${SW_DATA_SOURCE_USE_SERVER_PREP_STMTS:true}
metadataQueryMaxSize: ${SW_STORAGE_MYSQL_QUERY_MAX_SIZE:5000}
maxSizeOfArrayColumn: ${SW_STORAGE_MAX_SIZE_OF_ARRAY_COLUMN:20}
numOfSearchableValuesPerTag: ${SW_STORAGE_NUM_OF_SEARCHABLE_VALUES_PER_TAG:2}
将mysql连接驱动放入 oap-libs 里面
重启 skywalking,会看到数据库内生成了很多表
sakwarlking 采用无侵入式的agent参数来实现,微服务在启动时加入agent参数即可注册进skywalking
#!/bin/sh
# SkyWalking Agent配置
export SW_AGENT_NAME=springboot‐skywalking‐demo #服务名字,一般使用`spring.application.name`
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800 #配置skywalking服务端地址
export SW_AGENT_SPAN_LIMIT=2000 #配置链路的最大Span数量,默认为 300。
export JAVA_AGENT=‐javaagent:/usr/local/soft/apache‐skywalking‐apm‐bin‐es7/agent/skywalking‐agent.jar #skywalking agen包的路径
java $JAVA_AGENT ‐jar springboot‐skywalking‐demo‐0.0.1‐SNAPSHOT.jar #jar启动
idea开发环境下可以如下配置
-Dserver.port=7002 -javaagent:C:\Users\19943\Desktop\springCloud\springCloud-Alibaba\App\apache-skywalking-apm-bin-es7\agent\skywalking-agent.jar -DSW_AGENT_NAME=dept -DSW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
如果是gateway网关微服务还需要将agent\optional-plugins(可选插件)下的gateway.jar放入agent\plugins下开启spring-cloud-gateway的支持
sakwalking默认只对controller接口进行调用记录,如果我们想对service层也进行监控,就需要自定义了
1 引入依赖
<dependency>
<groupId>org.apache.skywalkinggroupId>
<artifactId>apm‐toolkit‐traceartifactId>
<version>8.5.0version>
dependency>
2 在service层的方法上加上@Trace注解,让skywalking记录这个接口
我们还可以为追踪链路增加其他额外的信息,比如记录参数和返回信息。实现方式:在方法上增加@Tag或者@Tags。
@Trace //使 skywalking管理此方法
@Tags({@Tag(key = "param", value = "arg[0]"), //配置要记录的参数
@Tag(key = "user", value = "returnedObj")})//配置记录返回值
public boolean select(String name){
return baseMapper.insert(new TableOrder(null,name)) == 1;
}
logback官方配置
log4j官方配置
log4j2j官方配置
springboot默认使用logback日志框架,这里使用logback记录日志
1 引入依赖
<!‐‐ apm‐toolkit‐logback‐1.x ‐‐>
<dependency>
<groupId>org.apache.skywalkinggroupId>
<artifactId>apm‐toolkit‐logback‐1.xartifactId>
<version>8.5.0version>
dependency>
2 添加logback-spring.xml文件,并配置 %tid 占位符
<configuration>
<!‐‐ 引入 Spring Boot 默认的 logback XML 配置文件 ‐‐>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!‐‐ 日志的格式化 ‐‐>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<Pattern>-%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) [%tid] %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wExlayout>
encoder>
appender>
<!‐‐ 设置 Appender ‐‐>
<root level="INFO">
<appender‐ref ref="console"/>
root>
configuration>
gRPC报告程序可以将收集到的日志转发到SkyWalking OAP服务器上,然后我们直接通过skywalking即可看到日志
1 导入依赖
<dependency>
<groupId>org.apache.skywalkinggroupId>
<artifactId>apm-toolkit-logback-1.xartifactId>
<version>8.5.0version>
dependency>
2.配置logback-spring.xml 新增 配置
<appender name="grpc-log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{tid}] [%thread] %-5level %logger{36} -%msg%nPattern>
layout>
encoder>
appender>
<!‐‐ 设置 Appender ‐‐>
<root level="INFO">
<appender‐ref ref="console"/>
<appender‐ref ref="grpc-log"/>
root>