Nacos配置管理
统一配置管理
- 配置更改热更新
将配置交给Nacos管理的步骤:
- 在Nacos中添加配置文件
- 在微服务中引入nacos的config依赖
- 在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件
- 在Nacos中添加配置信息
- 在弹出表单中填写配置信息
- 配置文件id:[服务名称]-[profile].[后缀名],如userservice-dev.yaml
- 分组:默认即可
- 格式:目前支持yaml和properties
-
引入Nacos的配置管理客户端依赖:
<!--nacos配置管理依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
-
在userservice中的resource目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml:
spring: application: name: userservice # 服务名称 profiles: active: dev # 环境 cloud: nacos: server-addr: localhost:8848 # nacos地址 config: file-extension: yaml # 文件后缀名
在user-service中将pattern.dateformat这个属性注入到UserController中做测试:
@Value("${pattern.dateformat}")
private String dateFormat;
@GetMapping("/now")
public String now() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateFormat, Locale.CHINA));
}
配置自动刷新
Nacos中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置实现:
-
方式一:在@Value注入的变量所在类上添加注解@RefreshScope
@Slf4j @RestController @RefreshScope @RequestMapping("/user") public class UserController { @Value("${pattern.dateformat}") private String dateFormat; ... }
-
方式二:使用@ConfigurationProperties注解(直接注入到某个类的成员变量中)
@Data @Component @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateFormat; }
注意事项:
- 不是所有的配置都适合放到配置中心,维护起来比较麻烦
- 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义配置
多环境共享配置
微服务启动时会从nacos读取多个配置文件:
- [spring.application.name]-[spring.profiles.active].yaml,例如:users
ervice-dev.yaml - [spring.application.name].yaml,例如: userservice.yaml
无论profile如何变化,[spring.application.name].yaml这个文件一定会加载
因此多环境共享配置可以写入这个文件
多种配置的优先级:
- 服务名-profile.yaml > 服务名称.yaml > 本地配置
- userservice-dev-.yaml > userservice.yaml > 本地配置文件
Nacos集群搭建
Nacos生产环境下一定要部署为集群状态
搭建集群的基本步骤:
-
搭建数据库,初始化数据库表结构
Nacos默认数据存储在内嵌数据库Derby中,不属于生产可用的数据库。官方推荐的最佳实践是使用带有主从的高可用数据库集群,此处使用单点的数据库为例。
首先新建一个数据库,命名为nacos,而后导入下面的SQL(sql文件位于nacos\conf\nacos-mysql.sql)
CREATE TABLE `config_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) DEFAULT NULL, `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `c_desc` varchar(256) DEFAULT NULL, `c_use` varchar(64) DEFAULT NULL, `effect` varchar(64) DEFAULT NULL, `type` varchar(64) DEFAULT NULL, `c_schema` text, PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_aggr */ /******************************************/ CREATE TABLE `config_info_aggr` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) NOT NULL COMMENT 'group_id', `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', `content` longtext NOT NULL COMMENT '内容', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_beta */ /******************************************/ CREATE TABLE `config_info_beta` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_tag */ /******************************************/ CREATE TABLE `config_info_tag` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(50) DEFAULT NULL COMMENT 'source ip', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_tags_relation */ /******************************************/ CREATE TABLE `config_tags_relation` ( `id` bigint(20) NOT NULL COMMENT 'id', `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `nid` bigint(20) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`nid`), UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = group_capacity */ /******************************************/ CREATE TABLE `group_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_group_id` (`group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = his_config_info */ /******************************************/ CREATE TABLE `his_config_info` ( `id` bigint(64) unsigned NOT NULL, `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `data_id` varchar(255) NOT NULL, `group_id` varchar(128) NOT NULL, `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL, `md5` varchar(32) DEFAULT NULL, `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `src_user` text, `src_ip` varchar(50) DEFAULT NULL, `op_type` char(10) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`nid`), KEY `idx_gmt_create` (`gmt_create`), KEY `idx_gmt_modified` (`gmt_modified`), KEY `idx_did` (`data_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = tenant_capacity */ /******************************************/ CREATE TABLE `tenant_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表'; CREATE TABLE `tenant_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `kp` varchar(128) NOT NULL COMMENT 'kp', `tenant_id` varchar(128) default '' COMMENT 'tenant_id', `tenant_name` varchar(128) default '' COMMENT 'tenant_name', `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info'; CREATE TABLE `users` ( `username` varchar(50) NOT NULL PRIMARY KEY, `password` varchar(500) NOT NULL, `enabled` boolean NOT NULL ); CREATE TABLE `roles` ( `username` varchar(50) NOT NULL, `role` varchar(50) NOT NULL, UNIQUE INDEX `idx_user_role` (`username` ASC, `role` ASC) USING BTREE ); CREATE TABLE `permissions` ( `role` varchar(50) NOT NULL, `resource` varchar(255) NOT NULL, `action` varchar(8) NOT NULL, UNIQUE INDEX `uk_role_permission` (`role`,`resource`,`action`) USING BTREE ); INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
-
下载nacos安装包
-
配置nacos
进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf,然后添加内容:
127.0.0.1:8845 127.0.0.1:8846 127.0.0.1:8847
然后修改application.properties文件,添加数据库配置
spring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC db.user.0=root db.password.0=root
-
启动nacos集群
将nacos文件夹复制三份,分别命名为:nacos1、nacos2、nacos3
然后分别修改三个文件夹中的application.properties,
nacos1:
server.port=8845
nacos2:
server.port=8846
nacos3:
server.port=8847
然后分别启动三个nacos节点:
startup.cmd
-
nginx反向代理
修改conf/nginx.conf文件,配置如下:
upstream nacos-cluster { server 127.0.0.1:8845; server 127.0.0.1:8846; server 127.0.0.1:8847; } server { listen 80; server_name localhost; location /nacos { proxy_pass http://nacos-cluster; } }
而后在浏览器访问:http://localhost/nacos即可。
修改代码中application.yml文件配置如下:
spring: cloud: nacos: server-addr: localhost:80 # Nacos地址
http客户端Feign
RestTemplate方式调用存在的问题
先来看我们以前利用RestTemplate发起远程调用的代码:
String url = "http: //userservice/user/" + order.getUserId();
User user = restTemplate.getFor0bject(url, User.class);
存在下面的问题:
- 代码可读性差,编程体验不统一
- 参数复杂URL难以维护
Feign的介绍
Feign是一个声明式的http客户端,官方地址: https://github.com/OpenFeign/feign
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
定义和使用Feign客户端
使用Feign的步骤如下∶
-
引入依赖:
<!--feign客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在order-service的启动类添加注解开启Feign的功能:
@MapperScan("cn.itcast.order.mapper") @SpringBootApplication @EnableFeignClients public class OrderApplication { ... }
-
编写Feign客户端:
package cn.itcast.order.clients; import cn.itcast.order.pojo.User; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") User FindById(@PathVariable("id") Long id); }
-
用Feign客户端代替RestTemplate
@Autowired private UserClient userClient; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2.用Feign远程调用 User user = userClient.findById(order.getUserId()); // 3.封装User到Order order.setUser(user); // 4.返回 return order; }
主要是基于SpringMVC的注解来声明远程调角的信息,比如:
- 服务名称: userservice
- 请求方式:GET
- 请求路径:/user/ {id}
- 请求参数: Long id
- 返回值类型:User
自定义Feign的配置
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:
一般我们需要配置的就是日志级别。
配置Feign日志有两种方式:
- 方式一:配置文件方式
-
全局生效:
feign: client: config: default: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置 loggerLevel: FULL #日志级别
-
局部生效
feign: client: config: userservice: #这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置 loggerLevel: FULL #日志级别
- 方式二: java代码方式
需要先声明一个Bean:
package cn.itcast.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel() {
return Logger.Level.BASIC;
}
}
- 而后如果是全局配置,则把它放到@EnableFeignClients这个注解中:
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
public class OrderApplication {
...
}
- 如果是局部配置,则把它放到@FeignClient这个注解中:
@FeignClient(value = "userservice", configuration= FeignClientConfiguration.class)
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
Feign的性能优化
Feign底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient :支持连接池
- OKHttp:支持连接池
因此优化Feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或none
Feign的性能优化-连接池配置
Feign添加Httpclient的支持:
引入依赖:
<!--HttpClient依赖-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置连接池:
feign:
httpclient:
enabled: true # 支持HttpClient的开关
max-connections: 200 # 最大连接数
max-connections-per-route: 50 #单个路径的最大连接数
Feign的最佳实践
方式一(继承)∶给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
- 服务紧耦合
- 父接口参数列表中的映射不会被继承
方式二(抽取)︰将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
抽取FeignClient
实现最佳实践方式二的步骤如下∶
-
首先创建一个module,命名为feign-api,然后引入feign的starter依赖
-
将order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
-
在order-service中引入feign-api的依赖
-
修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。有两种方式解决:
-
方式一:指定FeignClient所在包
@EnableFeignclients(basePackages = "cn.itcast.feign.clients")
-
方式二︰指定FeignClient字节码
@EnableFeignclients(clients = {Userclient.class})
统一网关Gateway
为什么需要网关
网关功能:
-
身份认证和权限校验
-
服务路由、负载均衡
-
请求限流
网关的技术实现
在SpringCloud中网关的实现包括两种:
- gateway
- zuul
Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
搭建网关服务
搭建网关服务的步骤:
-
创建新的module,引入SpringCloudGateway的依赖和nacos的服务发现依赖:
<!--nacos服务注册发现依赖--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!--网关gateway依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
-
编写路由配置及nacos地址
server: port: 10010 # 网关端口 spring: application: name: gateway # 服务名称cloud : cloud: nacos: server-addr: localhost:8848 # nacos地址 gateway: routes: # 网关路由配置 - id: user-service # 路由id,自定义,必须唯一 # uri: http ://127.0.0.1:8081 # 路由的目标地址 http就是固定地址 uri: lb://userservice # 路由的目标地址lb就是负载均衡,后面跟服务名称 predicates: # 路由断言,判断请求是否符合规则 - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合 - id: order-service uri: lb://orderservice predicates: - Path=/order/**
网关搭建步骤:
- 创建项目,引入nacos服务发现和gateway依赖
- 配置application.yml,包括服务基本信息、nacos地址、路由
路由配置包括:
- 路由id:路由的唯一标示
- 路由目标(uri) :路由的目标地址,http代表固定地址,lb代表根
据服务名负载均衡 - 路由断言( predicates) :判断路由的规则,
- 路由过滤器( filters) :对请求或响应做处理
路由断言工厂Route Predicate Factory
网关路由可以配置的内容包括:
- 路由id:路由唯一标示
- uri:路由目的地,支持lb和http两种
- predicates:路由断言,判断请求是否符合要求,符合则转发到路由目的地
- filters:路由过滤器,处理请求或响应
我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取开处理,转受为路出判断的条件
Spring提供了11种基本的Predicate工厂:
路由过滤器GatewayFilter
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:
Spring提供了31种不同的路由过滤器工厂。例如:
更多过滤器可以查看文档Spring Cloud Gateway 中文文档 (springdoc.cn)
默认过滤器
如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称cloud :
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,必须唯一
# uri: http ://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://userservice # 路由的目标地址lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
# filters: # 指定路由的过滤器
# - AddRequestHeader=MyKey,MyValue
default-filters: # 全局过滤器
- AddRequestHeader=MyKey,MyValue
接收参数:
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
@RequestHeader(value = "MyKey", required = false) String myKey) {
System.out.println("myKey:" + myKey);
return userService.queryById(id);
}
全局过滤器GlobalFilter
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。
区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口。
- 案例
定义全局过滤器,拦截并判断用户身份
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件。
- 参数中是否有authorization,
- authorization参数值是否为admin
如果同时满足则放行,否则拦截
-
步骤:自定义过滤器
自定义类,实现GlobalFilter接口,设置优先级:
//@Order(-1) //设置优先级,也可以通过实现Ordered接口设置(此处采用接口设置) @Component public class AuthorizeFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1.获取请求参数 ServerHttpRequest request = exchange.getRequest(); MultiValueMap<String, String> params = request.getQueryParams(); //2.获取参数中的authorization参数 String author = params.getFirst("authorization"); //3.判断参数值是否等于admin if ("admin".equals(author)) { //4.是,放行 return chain.filter(exchange); } //5.否,拦截 //5.1设置状态码 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //5.2拦截请求 return exchange.getResponse().setComplete(); } //设置优先级 @Override public int getOrder() { return -1; } }
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器
- 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
- GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
- 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
- 当过滤器的order值一样时,会按照defaultFilter >路由过滤器>GlobalFilter的顺序执行。
跨域问题处理
跨域:域名不一致就是跨域,主要包括:
- 域名不同: www.taobao.com和 www.taobao.org 和 www.jd.com和miaosha.jd.com
- 域名相同,端口不同: localhost:8080和localhost:8081
- 协议不同:http和https
跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
解决方案:CORS
网关处理跨域采用的同样是CORS方案,并且只需要简单配置即可实现:
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期