微服务组件:网关服务
一、网关
1、为什么需要网关
我们已经了解了微服务的各个基础组件,这些组件已经可以构建一个简单的微服务模型:

注册中心:Eureka
和consul
,用于将各个微服务的信息注册,为了让其他的微服务知道这些信息。方便调用
服务调用:声明式调用组件OpenFeign
,为了解决服务的集群调用,需要引入负载均衡组件Ribbon
。
容错保护:在服务运行时,可能会出现雪崩,服务器宕机等。为了使服务系统更加健壮,需要引入容错保护组件Hxstrix
,实现线程隔离和熔断机制。
上面的系统架构设计并没有问题,但是在开发和维护时难度会更大。
客户端发出请求,前端会根据这些请求发送给后端。因此前端需要维护大量的服务请求地址。
为了保证服务的对外安全性,我们都会添加权限校验机制,大规模的微服务会使校验逻辑变得复杂。
为了解决这些常见的架构问题,API网关的概念应运而生。
2、网关的概念
网关统一服务入口,可方便实现对平台众多服务接口进行管控。
1 2 3
| # 网关 = 路由转发 + 过滤器 路由转发:接收一切外界请求,转发到后端的微服务上去; 在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成
|
网关的定义类似于面向对象设计模式中的门面模式,它就像是整个微服务系统对客户端的门面。
1 2 3
| # 门面模式(Facade) 也叫外观模式,通过定义一个一致的接口,用于屏蔽内部子系统的细节。 使得调用方只需跟这个接口发生调用,而无需关心内部细节。
|
二、GateWay实例
1、路由转发的实现
两个服务,商品服务和用户服务。
1 2
| user 端口号 8881 product 端口号 8882
|
1 2 3 4 5
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
|
编写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| spring: application: name: gateway cloud: consul: host: localhost port: 8500 gateway: routes: - id: user_route uri: http://localhost:8881/ predicates: - Path=/user/**
- id: product_route uri: http://localhost:8882/ predicates: - Path=/product/** server: port: 8880
|
访问http://localhost:8880/user/findOne?id=8

这就是服务的路由转发。
2、集群服务的负载均衡
将用户服务复制一份
1 2 3 4 5 6 7 8 9
| @GetMapping("/user/findAllByFeign") public Map<String, Object> findAll() throws InterruptedException { log.info("通过使用OpenFeign组件调用服务"); Map<String,Object> map = new HashMap<>(); map.put("当前服务的端口号",port); log.info("当前端口号是:" + port); return map; }
|
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| spring: application: name: getway cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} gateway: routes: - id: user_route uri: lb://user predicates: - Path=/user/**
- id: product_route uri: lb://product predicates: - Path=/product/**
server: port: 8880
|
访问http://localhost:8880/user/findAllByFeign
,会按照负载均衡算法来分配请求到哪个服务,默认的负载均衡算法是轮询。


三、过滤器
前面说过,网关有两个作用,路由转发和过滤器。
网关中的过滤器有什么用:权限校验、限流以及监控
https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/
1、使用predicate
1.1、服务上线时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| spring: application: name: getway cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} gateway: routes: - id: user_route uri: lb://user predicates: - Path=/user/** - After=2021-07-15T11:33:33.993+08:00[Asia/Shanghai]
- id: product_route uri: lb://product predicates: - Path=/product/**
server: port: 8880
|
同样,也有服务下线的时间
1 2 3
| - Before=2020-07-21T11:33:33.993+08:00[Asia/Shanghai]
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
|
1.2、请求头
1 2 3 4
| predicates: - Path=/user/** - Header=head_Id,666
|
我们通过Postman来做试验,没有加请求头:

我们添加一个请求头:

1.3、添加cookie
1 2 3 4
| predicates: - Path=/user/** - Header=head_Id,666 - Cookie=username,jiang
|

1.5、设置请求方法
1 2 3 4 5
| predicates: - Path=/user/** - Header=head_Id,666 - Cookie=username,jiang - Method=POST
|

2、自定义过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Configuration @Slf4j public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("进入过滤器"); log.info("查看请求是否带有id参数"); List<String> list = exchange.getRequest().getQueryParams().get("id"); if(list != null){ log.info("带有参数,可以访问"); return chain.filter(exchange); }
log.info("请求参数中 没有 id 拒绝访问"); return exchange.getResponse().setComplete(); }
@Override public int getOrder() { return -1; } }
|
请求中添加参数

可以访问
