springboot2.x出来也有一小段时间了,之前一直钟情于vert.x的响应式编程,随着springboot2.x和spring-webflux的整合,那就结合springcloud全家桶玩起来~ 🥕
微服务中网关至关重要,相应的解决方案也比较多,像 Nginx+ Lua ,Spring Cloud Zuul, Spring Cloud Gateway(spring不再继续兼容zuul2.0,自研的网关中间件)。
因为Spring Cloud Gateway尚在观望,就先用zuul来做网关,比较坑的是它的很多功能需要自行实现。
动态路由
实现动态路由的方案一般有两种:
- DiscoveryClientRouteLocator的重新覆盖
- 实现了RefreshableRouteLocator接口
两种方案选了第二种去尝试,具体实现有大佬已经写过文章了,就不赘述了 Zuul动态路由。看了下相应的源码,是通过事件来刷新路由。为什么要这样实现呢,其实看了源码会发现,Spring Cloud Zuul是基于spring的事件驱动模型。(PS. 好多组件都用了这个模式)
源码中比较关键的是实现了ApplicationListener监听器的ZuulRefreshListener:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21//路由刷新监听器
private static class ZuulRefreshListener
implements ApplicationListener<ApplicationEvent> {
private ZuulHandlerMapping zuulHandlerMapping;
private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent
|| event instanceof RefreshScopeRefreshedEvent
|| event instanceof RoutesRefreshedEvent) {
//设置为脏,下一次匹配到路径时,如果发现为脏,则会去刷新路由信息
this.zuulHandlerMapping.setDirty(true);
}
else if (event instanceof HeartbeatEvent) {
if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
this.zuulHandlerMapping.setDirty(true);
}
}
}
}
那我们只需要通过publish RoutesRefreshedEvent就可以触发。
而具体的路由定位器可以看DiscoveryClientRouteLocator
,它主要从DiscoveryClient(如Eureka,consul)发现路由信息。
集群通知
上面虽然实现了动态路由,但是现在的服务为了保证高可用,不可能只有一个节点,网关也不例外。那每次要更新网关的路由,就要一个个节点去触发更新,那就太不优雅了。所以再折腾下,以达到触发单个节点,可以通知整个集群进行更新。
首先我们需要引入1
2
3
4<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
Spring Cloud Bus
Spring Cloud Bus的主要任务是将Spring的事件处理机制(又是事件驱动模型)和消息中间件消息的发送和接收整合起来,可以实现多个节点之间的通信,正符合我们的需求,下面就混带着源码和扩展来讲下如何实现。
创建 endpoint
首先,我们可以参考源码中的端点,如RefreshBusEndpoint
来实现我们自己的端点:AutoRouteEndpoint
1 | "route-refresh") (id = |
1 |
|
接收消息
然后主要是注意源码中的BusAutoConfiguration
类,下面是接受消息的代码:
1 | //监听RemoteApplicationEvent事件 |
通过@EventListener
来注册监听者,简化了以前需要实现ApplicationListener
(像上面的ZuulRefreshListener
),我们也实现一个自己的监听。
1 |
|
@EventListener
具体是如何实现注册呢,需要通过EventListenerFactory
的实现类,然后跟ApplicationListenerMethodAdapter
就清晰了,这里就不展开了。
1 | public class DefaultEventListenerFactory implements EventListenerFactory, Ordered { |
有了Listener来接收消息,那么我们还需要一个更新路由的event:
1 | public class AutoRouteEvent extends RemoteApplicationEvent { |
发送消息
代码同样在BusAutoConfiguration
类
1 | //消息的消费,也是事件的发起 |
一样通过注解来注册,当接收到消息,根据消息的来源,目的地(destination)配置等信息,将数据转化为RemoteApplicationEvent
对象,再次publish到spring context中。这一块代码可以复用,不需要重写。
自此我们就已经实现了动态路由和集群通知了~
实现的代码在 springcloud-gateway
Spring Cloud Actuator
Spring Cloud Bus中的endpoint是依赖于Actuator,它的主要作用是用于监控与管理。提供了许多端点,可以查看信息,也可以自定义端点(像上面的route-refresh)。
Actuator 部分端点:
HTTP 方法 | 路径 | 描述 |
---|---|---|
GET | /beans | 描述应用程序上下文里全部的Bean,以及它们的关系 |
GET | /health | 健康检查 |
GET | /env | 获取全部环境属性 |
GET | /env/{toMatch} | 根据名称获取特定的环境属性值 |
GET | /configprops | 描述配置属性(包含默认值)如何注入Bean |
GET | /mappings | 描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系 |
GET | /metrics | 报告各种应用程序度量信息,比如内存用量和HTTP请求计数 |
GET | /metrics/{requiredMetricName} | 报告指定名称的应用程序度量值 |
POST | /bus-refresh | 端点手动刷新配置 |
GET | /httptrace | 提供基本的HTTP请求跟踪信息(时间戳、HTTP头等) |
需要注意的是,对于spring-boot2.x 需要自己开放端点,配置如下:
1 | management: |