跳转到目录
Gateway网关是我们服务的守门神,所有微服务的统一入口。Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
在Gateway之前,SpringCloud并不自己开发网关,可能是觉得Netflix公司的Zuul不行吧,然后自己就写了一个,也是替代Netflix Zuul。其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
本身也是一个微服务,需要注册到Eureka
搭建网关微服务,实现服务路由分发。
实现步骤:
实现过程:
启动引导类开启注册中心Eureka客户端发现
@SpringBootApplication
@EnableDiscoveryClient// 开启Eureka客户端发现功能
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
编写基础配置
在gateway_server中创建application.yml文件,配置
# 端口
server.port: 10010
# 应用名
spring.application.name: api-gateway
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
编写路由规则
修改gateway_server的配置文件application.yml,配置网关内容
spring:
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: user-service-route
# 路由服务地址
uri: http://127.0.0.1:9091
# 断言
predicates:
- Path=/user/**
将符合path
规则的请求,路由到uri
参数指定地址。
举例:http://localhost:10010/user/findById?id=1 路由转发到http://localhost:9091/user/findById?id=1
启动GatewayApplication进行测试
刚才路由规则中,我们把路径对应服务地址写死了!如果服务提供者集群的话,这样做不合理。应该是根据服务名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由!
修改映射配置:通过服务名称获取
因为已经配置了Eureka客户端,可以从Eureka获取服务的地址信息,修改application.yml文件如下
# 注解版
spring:
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: user-service-route
# 路由地址
# uri: http://127.0.0.1:9091
# 采用lb协议,会从Eureka注册中心获取服务请求地址
# 路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务
# 规则:lb协议+服务名称
uri: lb://user-service
# 路由拦截地址(断言)
predicates:
- Path=/user/**
路由配置中uri所用的协议为lb时,gateway将把user-service解析为实际的主机和端口,并通过Ribbon进行负载均衡。
第一:添加前缀:
在gateway中可以通过配置路由的过滤器PrefixPath 实现映射路径中的前缀添加。可以起到隐藏接口地址的作用,避免接口地址暴露。
配置请求地址添加路径前缀过滤器
spring:
cloud:
gateway:
routes:
- id: user-service-route # 路由id,可以随意写
# 代理服务地址;lb表示从Eureka中获取具体服务
uri: lb://user-service
# 路由断言,配置映射路径
predicates:
- Path=/**
# 请求地址添加路径前缀过滤器
filters:
- PrefixPath=/user
重启GatewayApplication
配置完成的效果:
配置 | 访问地址 | 路由地址 |
---|---|---|
PrefixPath=/user | localhost:10010/findById?id=1 | localhost:9091/user/findById?id=1 |
PrefixPath=/user/abc | localhost:10010/findById?id=1 | localhost:9091/user/abc/findById?id=1 |
第二:去除前缀:
在gateway中通过配置路由过滤器StripPrefix,实现映射路径中地址的去除。通过StripPrefix=1来指定路由要去掉的前缀个数。如:路径/api/user/1将会被路由到/user/1。
配置去除路径前缀过滤器
spring:
cloud:
gateway:
routes:
- id: user-service-route # 路由id,可以随意写
# 代理服务地址;lb表示从Eureka中获取具体服务
uri: lb://user-service
# 路由断言,配置映射路径
predicates:
- Path=/**
# 去除路径前缀过滤器
filters:
- StripPrefix=1
重启GatewayApplication
访问查看效果
配置 | 访问地址 | 路由地址 |
---|---|---|
StripPrefix=1 | localhost:10010/api/user/findById?id=1 | localhost:9091/user/findById?id=1 |
StripPrefix=2 | localhost:10010/aa/api/user/findById?id=1 | localhost:9091/user/findById?id=1 |
过滤器作为网关的其中一个重要功能,就是实现请求的鉴权。前面的路由前缀
章节中的功能也是使用过滤器实现的。
Gateway自带过滤器有几十个,常见自带过滤器有:
过滤器名称 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀 |
PrefixPath | 对匹配上的请求路径添加前缀 |
详细说明官方链接
使用场景:
**过滤器类型:**Gateway有两种过滤器
配置全局过滤器:
对输出的响应设置其头部属性名称为i-love,值为itheima
修改配置文件
spring:
cloud:
gateway:
# 配置全局默认过滤器
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=i-love,itheima
Spring Cloud Gateway 的 Filter 的执行顺序有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用。
这里的 pre
和post
可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。
需求: 模拟登录校验。
实现步骤:
实现过程:
在gateway_server中,全局过滤器类MyGlobalFilter,实现GlobalFilter和 Ordered接口
编写业务逻辑代码判断:
如果请求中有token参数,则认为请求有效,放行
如果没有则拦截提示未授权
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("-----------------全局过滤器MyGlobalFilter---------------------");
//1、获取参数中的token,以及token的值
String token = exchange.getRequest().getQueryParams().getFirst("token");
//2、如果token的值为空,则拦截
if (StringUtils.isBlank(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 定义过滤器执行顺序
* 返回值越小,越靠前执行
* @return
*/
@Override
public int getOrder() {
return 0;//
}
}
访问:http://localhost:10010/api/user/findById?id=1
访问:http://localhost:10010/api/user/findById?id=1&token=11
跳转到目录
分布式系统中,由于服务数量非常多,配置文件分散在不同微服务项目中,管理极其不方便。为了方便配置文件集中管理,需要分布式配置中心组件。在Spring Cloud中,提供了Spring Cloud Config,它支持配置文件放在配置服务的本地,也支持配置文件放在远程仓库Git(GitHub、码云)。配置中心本质上是一个微服务,同样需要注册到Eureka服务中心!
一句话概括:统一管理所有微服务配置文件的一个微服务
配置中心,也是一个微服务,需要注册到注册中心
实现步骤:
实现过程:
启动类:创建配置中心工程config_server的启动类ConfigServerApplication
@SpringBootApplication
@EnableDiscoveryClient//开启Eureka客户端发现功能
@EnableConfigServer //开启配置服务支持
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
配置文件:创建配置中心工程config_server的配置文件application.yml
# 端口
server.port: 12000
# 应用名称
spring.application.name: config-server
# git仓库地址
spring.cloud.config.server.git.uri: https://gitee.com/liuyaxiong01/itheima-spring-cloud-config.git
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
启动测试:启动eureka注册中心和配置中心;
关于application.yml和bootstrap.yml文件的说明:
目标:改造user_service工程,配置文件不再由微服务项目提供,而是从配置中心获取。
实现步骤:
实现过程:
添加依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
修改配置
删除user_service工程的application.yml文件
创建user_service工程bootstrap.yml配置文件,配置内容如下
# 注册中心地址
eureka.client.service-url.defaultZone: http://127.0.0.1:10086/eureka
# 配置中心相关配置
# 使用配置中心
spring.cloud.config.discovery.enabled: true
# 配置中心服务id
spring.cloud.config.discovery.service-id: config-server
# 与远程仓库中的配置文件的application和profile保持一致,{application}-{profile}.yml
spring.cloud.config.name: user
spring.cloud.config.profile: dev
# 远程仓库中的分支保持一致
spring.cloud.config.label: master
启动测试:
复现问题步骤:
修改远程Git配置
修改UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Value("${server.port}")
private String port;
@Value("${test.hello}")
private String name;
@Autowired
UserService userService;
//查询所有
@RequestMapping("/findAll")
public List<User> findAll() {
return userService.findAll();
}
//根据id查询
@RequestMapping("/findById")
public User findById(Integer id) {
System.out.println("服务【"+port+"】被调用");
User user = userService.findById(id);
user.setNote("服务【"+port+"】被调用");
user.setName(name);
return user;
}
}
测试:
结论:通过浏览器输出结果发现,我们对于Git仓库中的配置文件的修改,并没有及时更新到user-service微服务,只有重启用户微服务才能生效。
SpringCloud Bus,解决上述问题,实现配置自动更新。
跳转到目录
Bus是用轻量的消息代理将分布式的节点连接起来,可以用于**广播配置文件的更改**或者服务的监控管理。Bus可以为微服务做监控,也可以实现应用程序之间互相通信。Bus可选的消息代理(消息队列)RabbitMQ和Kafka。
广播出去的配置文件服务会进行本地缓存。
注意:SpringCloudBus基于RabbitMQ实现,默认使用本地的消息队列服务,所以需要提前安装并启动RabbitMQ。安装参考./04资料/安装Windows RabbitMQ.pdf
安装注意事项:
所有操作均以管理员权限执行
系统的用户名不能为中文
安装路径也不能含有中文
目标:消息总线整合入微服务系统,实现配置中心的配置自动更新。不需要重启微服务。
改造步骤:
实现过程:
在config_server项目中加入Bus相关依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-busartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-stream-binder-rabbitartifactId>
dependency>
在config_server项目中修改application.yml
# RabbitMQ的服务地址
spring.rabbitmq.host: localhost
spring.rabbitmq.port: 5672
spring.rabbitmq.username: guest
spring.rabbitmq.password: guest
# 触发配置文件广播的地址
management.endpoints.web.exposure.include: bus-refresh
改造步骤:
实现过程:
在用户微服务user_service项目中加入Bus相关依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-busartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-stream-binder-rabbitartifactId>
dependency>
修改user_service项目的bootstrap.yml
# RabbitMQ的服务地址
spring.rabbitmq.host: localhost
spring.rabbitmq.port: 5672
spring.rabbitmq.username: guest
spring.rabbitmq.password: guest
改造用户微服务user_service项目的UserController
@RestController
@RequestMapping("/user")
@RefreshScope //刷新配置
public class UserController {
@Value("${server.port}")
private String port;
@Value("${test.hello}")
private String name;
@Autowired
UserService userService;
//查询所有
@RequestMapping("/findAll")
public List<User> findAll() {
return userService.findAll();
}
//根据id查询
@RequestMapping("/findById")
public User findById(Integer id) {
System.out.println("服务【"+port+"】被调用");
User user = userService.findById(id);
user.setNote("服务【"+port+"】被调用");
user.setName(name);
return user;
}
}
目标:当我们修改Git仓库的配置文件,用户微服务是否能够在不重启的情况下自动更新配置文件信息。
测试步骤:
说明:
消息总线实现消息分发过程: