无为教育网站,进一步强化网站建设,深圳网站建设哪家公司便宜,厦门高端模板建站目录 1 Nacos配置管理1.1 统一配置管理1.1.1 在nacos中添加配置文件1.1.2 从微服务拉取配置 1.2 配置热更新1.2.1 方式一1.2.2 方式二#xff08;推荐#xff09; 1.3.配置共享 2 搭建Nacos集群2.1 集群结构图2.2 搭建集群2.2.1 初始化数据库2.2.2 下载nacos2.2.3 配置Nacos2… 目录 1 Nacos配置管理1.1 统一配置管理1.1.1 在nacos中添加配置文件1.1.2 从微服务拉取配置 1.2 配置热更新1.2.1 方式一1.2.2 方式二推荐 1.3.配置共享 2 搭建Nacos集群2.1 集群结构图2.2 搭建集群2.2.1 初始化数据库2.2.2 下载nacos2.2.3 配置Nacos2.3.4 启动2.2.5 nginx反向代理2.2.6 优化 3 http客户端Feign3.1 Feign替代RestTemplate3.2 自定义配置3.2.1 方式一配置文件方式3.2.2 方式二Java代码方式 3.3 Feign使用优化3.4 最佳实践3.4.1 方式一继承方式不太推荐3.4.2 方式二抽取方式3.4.3 实现基于抽取的最佳实践 4 Gateway服务网关4.1 为什么需要网关4.2 gateway快速入门4.3 断言工厂4.4 过滤器工厂4.4.1 路由过滤器的种类4.4.2 请求头过滤器4.4.3 默认过滤器 4.5 全局过滤器4.5.1 全局过滤器作用4.5.2 自定义全局过滤器4.5.3 过滤器执行顺序 4.6.跨域问题4.6.1 什么是跨域问题4.6.2 模拟跨域问题4.6.3 解决跨域问题 1 Nacos配置管理
Nacos除了可以做注册中心同样可以做配置管理来使用。
1.1 统一配置管理
当微服务部署的实例越来越多达到数十、数百时逐个修改微服务配置就会让人抓狂而且很容易出错。我们需要一种统一配置管理方案可以集中管理所有实例的配置。 Nacos一方面可以将配置集中管理另一方可以在配置变更时及时通知微服务实现配置的热更新。
1.1.1 在nacos中添加配置文件 注意项目的核心配置需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好 1.1.2 从微服务拉取配置
微服务要拉取nacos中管理的配置并且与本地的application.yml配置合并才能完成项目启动。 但如果尚未读取application.yml又如何得知nacos地址呢 因此spring引入了一种新的配置文件bootstrap.yaml文件会在application.yml之前被读取流程如下 1引入nacos-config依赖 首先在user-service服务中引入nacos-config的客户端依赖
!--nacos配置管理依赖--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-config/artifactId
/dependency2添加bootstrap.yaml 然后在user-service中添加一个bootstrap.yaml文件内容如下
spring:application:name: userservice # 服务名称profiles:active: dev #开发环境这里是dev cloud:nacos:server-addr: localhost:8848 # Nacos地址config:file-extension: yaml # 文件后缀名这里会根据spring.cloud.nacos.server-addr获取nacos地址再根据 ${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}作为文件id来读取配置。 本例中就是去读取userservice-dev.yaml 3读取nacos配置 在user-service中的UserController中添加业务逻辑读取pattern.dateformat配置 在user-service中将pattern.dateformat这个属性注入到UserController中做测试
// 注入nacos中的配置属性
Value(${pattern.dateformat})
private String dateformat;
// 编写controller通过日期格式化器来格式化现在时间并返回
GetMapping(now)
public String now(){return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat, Locale.CHINA));
}重启服务访问页面成功显示
1.2 配置热更新
我们最终的目的是修改nacos中的配置后微服务中无需重启即可让配置生效也就是配置热更新。 要实现配置热更新可以使用两种方式
1.2.1 方式一
在Value注入的变量所在类上添加注解RefreshScope
1.2.2 方式二推荐
使用ConfigurationProperties注解代替Value注解。在user-service服务中添加一个类读取patterrn.dateformat属性
Component
Data
ConfigurationProperties(prefix pattern)
public class PatternProperties {private String dateformat;
}在UserController中使用这个类代替Value
Autowired
private PatternProperties patternProperties;
// 编写controller通过日期格式化器来格式化现在时间并返回
GetMapping(/now)
public String now(){return LocalDateTime.now().format(DateTimeFormatter.ofPattern(patternProperties.getDateformat(), Locale.CHINA));
}1.3.配置共享
其实微服务启动时会去nacos读取多个配置文件例如
[spring.application.name]-[spring.profiles.active].yaml例如userservice-dev.yaml[spring.application.name].yaml例如userservice.yaml 而[spring.application.name].yaml不包含环境因此可以被多个环境共享。
下面我们通过案例来测试配置共享
添加一个环境共享配置 我们在nacos中添加一个userservice.yaml文件 在user-service中读取共享配置 在user-service服务中修改PatternProperties类读取新添加的属性
在user-service服务中修改UserController添加一个方法
运行两个UserApplication使用不同的profile 这样UserApplication(8081)使用的profile是devUserApplication3(8083)使用的profile是test。 启动UserApplication和UserApplication3 结果 当nacos、服务本地同时出现相同属性时优先级有高低之分
2 搭建Nacos集群
Nacos生产环境下一定要部署为集群状态
2.1 集群结构图
官方给出的Nacos集群图 其中包含3个nacos节点然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx。 我们计划的集群结构 三个nacos节点的地址
节点ipportnacos1192.168.150.18845nacos2192.168.150.18846nacos3192.168.150.18847
2.2 搭建集群
搭建集群的基本步骤
搭建数据库初始化数据库表结构下载nacos安装包配置nacos启动nacos集群nginx反向代理
2.2.1 初始化数据库
Nacos默认数据存储在内嵌数据库Derby中不属于生产可用的数据库。 官方推荐的最佳实践是使用带有主从的高可用数据库集群环境有限这里我们以单点的数据库为例来讲解。 首先新建一个数据库命名为nacos而后导入下面的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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin COMMENTconfig_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin COMMENTconfig_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin COMMENTconfig_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin COMMENTconfig_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_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)
) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin COMMENTtenant_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);2.2.2 下载nacos
见SpringCloud实用篇1中nacos注册中心
2.2.3 配置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.platformmysqldb.num1db.url.0jdbc:mysql://127.0.0.1:3306/nacos?characterEncodingutf8connectTimeout1000socketTimeout3000autoReconnecttrueuseUnicodetrueuseSSLfalseserverTimezoneUTC
db.user.0root
db.password.01232.3.4 启动
将nacos文件夹复制三份分别命名为nacos1、nacos2、nacos3分别修改三个文件夹中的application.properties nacos1server.port8845nacos2server.port8846nacos3server.port8847 分别启动三个nacos节点startup.cmd
2.2.5 nginx反向代理
将下载好的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地址2.2.6 优化 实际部署时需要给做反向代理的nginx服务器设置一个域名这样后续如果有服务器迁移nacos的客户端也无需更改配置. Nacos的各个节点应该部署到多个不同服务器做好容灾和隔离
3 http客户端Feign
先来看我们以前利用RestTemplate发起远程调用的代码 存在下面的问题
代码可读性差编程体验不统一参数复杂URL难以维护
Feign是一个声明式的http客户端官方地址https://github.com/OpenFeign/feign 其作用就是帮助我们优雅的实现http请求的发送解决上面提到的问题。
3.1 Feign替代RestTemplate
Fegin的使用步骤如下
引入依赖 我们在order-service服务的pom文件中引入feign的依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency添加注解
在order-service的启动类添加注解开启Feign的功能
编写Feign的客户端
在order-service中新建一个接口内容如下
FeignClient(userservice)
public interface UserClient {GetMapping(/user/{id})User findById(PathVariable(id) Long id);
}这个客户端主要是基于SpringMVC的注解来声明远程调用的信息比如
服务名称userservice请求方式GET请求路径/user/{id}请求参数Long id返回值类型User
这样Feign就可以帮助我们发送http请求无需自己使用RestTemplate来发送了。
测试
修改order-service中的OrderService类中的queryOrderById方法使用Feign客户端代替RestTemplate
......
Autowired
private UserClient userClient;public Order queryOrderById(Long orderId) {// 1.查询订单Order order orderMapper.findById(orderId);// 2.用Fegin远程调用User user userClient.findById(order.getUserId());// 3.封装User到Orderorder.setUser(user);// 4.返回return order;
}3.2 自定义配置
Feign可以支持很多的自定义配置如下表所示
类型作用说明feign.Logger.Level修改日志级别包含四种不同的级别NONE、BASIC、HEADERS、FULLfeign.codec.Decoder响应结果的解析器http远程调用的结果做解析例如解析json字符串为java对象feign.codec.Encoder请求参数编码将请求参数编码便于通过http请求发送feign. Contract支持的注解格式默认是SpringMVC的注解feign. Retryer失败重试机制请求失败的重试机制默认是没有不过会使用Ribbon的重试
一般情况下默认值就能满足我们使用如果要自定义时只需要创建自定义的Bean覆盖默认Bean即可。 下面以日志为例来演示如何自定义配置。
3.2.1 方式一配置文件方式
基于配置文件修改feign的日志级别可以针对单个服务
feign: client:config: userservice: # 针对某个微服务的配置loggerLevel: FULL # 日志级别 也可以针对所有服务
feign: client:config: default: # 这里用default就是全局配置如果是写服务名称则是针对某个微服务的配置loggerLevel: FULL # 日志级别 日志的级别分为四种
NONE不记录任何日志信息这是默认值。BASIC仅记录请求的方法URL以及响应状态码和执行时间HEADERS在BASIC的基础上额外记录了请求和响应的头信息FULL记录所有请求和响应的明细包括头信息、请求体、元数据。
3.2.2 方式二Java代码方式
先声明一个类然后声明一个Logger.Level的对象
public class DefaultFeignConfiguration {Beanpublic Logger.Level feignLogLevel(){return Logger.Level.BASIC; // 日志级别为BASIC}
}如果要全局生效将其放到启动类的EnableFeignClients这个注解中
EnableFeignClients(defaultConfiguration DefaultFeignConfiguration .class) 如果是局部生效则把它放到对应的FeignClient这个注解中
FeignClient(value userservice, configuration DefaultFeignConfiguration .class) 3.3 Feign使用优化
Feign底层发起http请求依赖于其它的框架。其底层客户端实现包括
URLConnection默认实现不支持连接池性能不太好Apache HttpClient 支持连接池OKHttp支持连接池
因此优化Feign的性能主要包括
使用连接池代替默认的URLConnection日志级别最好用basic或none
这里我们用Apache的HttpClient来演示。
引入依赖
在order-service的pom文件中引入Apache的HttpClient依赖
!--httpClient的依赖 --
dependencygroupIdio.github.openfeign/groupIdartifactIdfeign-httpclient/artifactId
/dependency配置连接池
在order-service的application.yml中添加配置
feign:client:config:default: # default全局的配置loggerLevel: BASIC # 日志级别BASIC就是基本的请求和响应信息httpclient:enabled: true # 开启feign对HttpClient的支持max-connections: 200 # 最大的连接数max-connections-per-route: 50 # 每个路径的最大连接数3.4 最佳实践
所谓最近实践就是使用过程中总结的经验最好的一种使用方式。 观察可以发现Feign的客户端与服务提供者的controller代码非常相似 feign客户端
UserController 有没有一种办法简化这种重复的代码编写呢
3.4.1 方式一继承方式不太推荐
一样的代码可以通过继承来共享 1定义一个API接口利用定义方法并基于SpringMVC注解做声明。 2Feign客户端和Controller都集成改接口
优点
简单实现了代码共享
缺点
服务提供方、服务消费方紧耦合参数列表中的注解映射并不会继承因此Controller中必须再次声明方法、参数列表、注解
3.4.2 方式二抽取方式
将Feign的Client抽取为独立模块并且把接口有关的POJO、默认的Feign配置都放到这个模块中提供给所有消费者使用。 例如将UserClient、User、Feign的默认配置都抽取到一个feign-api包中所有微服务引用该依赖包即可直接使用。
3.4.3 实现基于抽取的最佳实践
抽取
首先创建一个module命名为feign-api 在feign-api中然后引入feign的starter依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-openfeign/artifactId
/dependency然后order-service中编写的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
在order-service中使用feign-api
首先删除order-service中的UserClient、User、DefaultFeignConfiguration等类或接口。 在order-service的pom文件中中引入feign-api的依赖
dependencygroupIdcn.itcast.demo/groupIdartifactIdfeign-api/artifactIdversion1.0/version
/dependency修改order-service中的所有与上述三个组件有关的导包部分改成导入feign-api中的包
重启测试
重启后发现服务报错了 这是因为UserClient现在在cn.itcast.feign.clients包下 而order-service的EnableFeignClients注解是在cn.itcast.order包下不在同一个包无法扫描到UserClient。
解决扫描包问题
方式一
指定Feign应该扫描的包
EnableFeignClients(basePackages cn.itcast.feign.clients)方式二推荐
指定需要加载的Client接口
EnableFeignClients(clients {UserClient.class})4 Gateway服务网关
Spring Cloud Gateway 是 Spring Cloud 的一个全新项目该项目是基于 Spring 5.0Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
4.1 为什么需要网关
Gateway网关是我们服务的守门神所有微服务的统一入口。
网关的核心功能特性
请求路由服务路由、负载均衡权限控制身份验证、权限校验限流
权限控制网关作为微服务入口需要校验用户是是否有请求资格如果没有则进行拦截。
路由和负载均衡一切请求都必须先经过gateway但网关不处理业务而是根据某种规则把请求转发到某个微服务这个过程叫做路由。当然路由的目标服务有多个时还需要做负载均衡。
限流当请求流量过高时在网关中按照下流的微服务能够接受的速度来放行请求避免服务压力过大。
架构图
在SpringCloud中网关的实现包括两种
gatewayzuul
Zuul是基于Servlet的实现属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux属于响应式编程的实现具备更好的性能。 4.2 gateway快速入门
下面我们就演示下网关的基本路由功能。基本步骤如下
创建SpringBoot工程gateway引入网关依赖编写启动类编写基础配置和路由规则启动网关服务进行测试
步骤一创建gateway服务引入依赖
创建服务
引入依赖
!--网关--
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-gateway/artifactId
/dependency
!--nacos服务发现依赖--
dependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId
/dependency步骤二编写启动类
package cn.itcast.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}
}步骤三编写基础配置和路由规则
创建application.yml文件内容如下
server:port: 10010 # 网关端口
spring:application:name: gateway # 服务名称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-serviceuri: lb://orderservicepredicates:- Path/order/**我们将符合Path 规则的一切请求都代理到 uri参数指定的地址。
本例中我们将 /user/**开头的请求代理到lb://userservicelb是负载均衡根据服务名拉取服务列表实现负载均衡。
步骤四重启测试
重启网关访问http://localhost:10010/user/1时符合/user/**规则请求转发到urihttp://userservice/user/1得到了结果
网关路由的流程图整个访问的流程如下
4.3 断言工厂 我们在配置文件中写的断言规则只是字符串这些字符串会被Predicate Factory读取并处理转变为路由判断的条件 例如Path/user/**是按照路径匹配这个规则是org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来 处理的 像这样的断言工厂在SpringCloudGateway还有十几个:
名称说明示例After是某个时间点后的请求- After2037-01-20T17:42:47.789-07:00[America/Denver]Before是某个时间点之前的请求- Before2031-04-13T15:14:47.43308:00[Asia/Shanghai]Between是某两个时间点之前的请求- Between2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]Cookie请求必须包含某些cookie- Cookiechocolate, ch.pHeader请求必须包含某些header- HeaderX-Request-Id, \dHost请求必须是访问某个host域名- Host.somehost.org,.anotherhost.orgMethod请求方式必须是指定方式- MethodGET,POSTPath请求路径必须符合指定规则- Path/red/{segment},/blue/**Query请求参数必须包含指定参数- Queryname, Jack或者- QuerynameRemoteAddr请求者的ip必须是指定范围- RemoteAddr192.168.1.1/24Weight权重处理
我们只需要掌握Path这种路由工程就可以了。
4.4 过滤器工厂
GatewayFilter是网关中提供的一种过滤器可以对进入网关的请求和微服务返回的响应做处理
4.4.1 路由过滤器的种类
Spring提供了31种不同的路由过滤器工厂。例如
名称说明AddRequestHeader给当前请求添加一个请求头RemoveRequestHeader移除请求中的一个请求头AddResponseHeader给响应结果中添加一个响应头RemoveResponseHeader从响应结果中移除有一个响应头RequestRateLimiter限制请求的流量
4.4.2 请求头过滤器
以AddRequestHeader 为例 需求给所有进入userservice的请求添加一个请求头Truthitcast is freaking awesome! 只需要修改gateway服务的application.yml文件添加路由过滤即可
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path/user/** filters: # 过滤器- AddRequestHeaderTruth, Itcast is freaking awesome! # 添加请求头当前过滤器写在userservice路由下因此仅仅对访问userservice的请求有效。
测试一下 修改一下UserController 重启服务访问localhost://10010/user/1查看控制台输出
4.4.3 默认过滤器
如果要对所有的路由都生效则可以将过滤器工厂写到default下。格式如下
spring:cloud:gateway:routes:- id: user-service uri: lb://userservice predicates: - Path/user/**default-filters: # 默认过滤项- AddRequestHeaderTruth, Itcast is freaking awesome! 4.5 全局过滤器
问题上一节学习的过滤器网关提供了31种但每一种过滤器的作用都是固定的。如果我们希望拦截请求做自己的业务逻辑则没办法实现。
4.5.1 全局过滤器作用
全局过滤器的作用也是处理一切进入网关的请求和微服务响应与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义处理逻辑是固定的而GlobalFilter的逻辑需要自己写代码实现。
定义方式是实现GlobalFilter接口
public interface GlobalFilter {/*** 处理当前请求有必要的话通过{link GatewayFilterChain}将请求交给下一个过滤器处理** param exchange 请求上下文里面可以获取Request、Response等信息* param chain 用来把请求委托给下一个过滤器 * return {code MonoVoid} 返回标示当前过滤器业务结束*/MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain);
}在filter中编写自定义逻辑可以实现下列功能
登录状态判断权限校验请求限流等
4.5.2 自定义全局过滤器 需求定义全局过滤器拦截请求判断请求的参数是否满足下面条件 参数中是否有authorization authorization参数值是否为admin 如果同时满足则放行否则拦截 实现
在gateway中定义一个过滤器AuthorizeFilter
Component
Order(-1)
public class AuthorizeFilter implements GlobalFilter {Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数ServerHttpRequest request exchange.getRequest();MultiValueMapString, String params request.getQueryParams();// 2.获取请求参数中的authorization 参数String auth params.getFirst(authorization);// 3.判断参数值是否等于 adminif (admin.equals(auth)){// 4.是放行return chain.filter(exchange);}// 5.否拦截// 5.1设置状态码用户能直观看到exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); //状态码401// 5.2拦截请求return exchange.getResponse().setComplete();}
}测试 访问locahost://10010/user/1?authorizationadmin 访问locahost://10010/user/1 4.5.3 过滤器执行顺序
请求进入网关会碰到三类过滤器当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后会将当前路由过滤器和DefaultFilter、GlobalFilter合并到一个过滤器链集合中排序后依次执行每个过滤器 排序的规则是什么呢
每一个过滤器都必须指定一个int类型的order值order值越小优先级越高执行顺序越靠前。GlobalFilter通过实现Ordered接口或者添加Order注解来指定order值由我们自己指定路由过滤器和defaultFilter的order由Spring指定默认是按照声明顺序从1递增。当过滤器的order值一样时会按照 defaultFilter 路由过滤器 GlobalFilter的顺序执行。
详细内容可以查看源码 org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters然后再加载某个route的filters然后合并。 org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器与前面的过滤器合并后根据order排序组织过滤器链 4.6.跨域问题
4.6.1 什么是跨域问题
跨域域名不一致就是跨域主要包括 域名不同 www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com 域名相同端口不同localhost:8080和localhost8081
跨域问题浏览器禁止请求的发起者与服务端发生跨域ajax请求请求被浏览器拦截的问题
解决方案CORS这个以前应该学习过这里不再赘述了。
4.6.2 模拟跨域问题
准备页面文件index.html
headmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0meta http-equivX-UA-Compatible contentieedgetitleDocument/title
/head
body
pre
spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:[/**]:allowedOrigins: # 允许哪些网站的跨域请求- http://localhost:8090- http://www.leyou.comallowedMethods: # 允许的跨域ajax的请求方式- GET- POST- DELETE- PUT- OPTIONSallowedHeaders: * # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期
/pre
/body
script srchttps://unpkg.com/axios/dist/axios.min.js/script
scriptaxios.get(http://localhost:10010/user/1?authorizationadmin).then(resp console.log(resp.data)).catch(err console.log(err))
/script
/html放入tomcat或者nginx这样的web服务器中启动并访问。可以在浏览器控制台看到下面的错误 从localhost:8090访问localhost:10010端口不同显然是跨域的请求。
4.6.3 解决跨域问题
在gateway服务的application.yml文件中添加下面的配置
spring:cloud:gateway:globalcors: # 全局的跨域处理add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题corsConfigurations:[/**]:allowedOrigins: # 允许哪些网站的跨域请求 - http://localhost:8090allowedMethods: # 允许的跨域ajax的请求方式- GET- POST- DELETE- PUT- OPTIONSallowedHeaders: * # 允许在请求中携带的头信息allowCredentials: true # 是否允许携带cookiemaxAge: 360000 # 这次跨域检测的有效期