系列文章
【开源】Sentinel控制台集群方案(使用Ignite解决单点故障问题)
一、前言
最近在项目中使用到了Sentinel来做限流与降级,并封装了一套切合项目的自动限流降级框架。在使用Sentinel的过程中发现如下两个问题:
1、Sentinel提供的控制台只能够支持单机部署,存在单点故障的问题
2、Sentinel默认只支持单机限流,如果需要支持集群限流则要自己实现并提供Token Server。
为了让Sentinel能够完美的应用到项目中,因此我们必须要解决这两个棘手的问题。经过分析选型我们最终设计出了如下方案:
1、使用Ignite来解决控制台故障的问题
2、使用Raft协议来构建高可用性能的Token Server
当前项目已经完成了Ignite协议的控制台集群方案的设计元开发,而Token Server方案还在开发中。因此文本先介绍基于Ignite的控制台集群方案。
PS:该项目已经开源,开源信息在文章末尾。后续Token Server完成之后也将为大家开源。
二、总体设计方案
1、什么是Ignite
Ignite是以内存为中心的分布式数据库、缓存和处理平台,用于事务性、分析性和流式工作负载,提供内存速度在PB级规模。
Ignite 为应用和不同的数据源之间提供一个高性能、分布式内存中数据组织管理的框架。它包含如下组件:
1.高级的集群化
2.数据网格(JCache)
3.流计算和CEP
4.计算网格
5.服务网格
6.Ignite文件系统
7.分布式数据结构
8.分布式消息
9.分布式事件
10.Hadoop加速器
11.Spark共享RDD
总的来说其功能很强大,但是为了实现控制台集群,我们只需要知道Ignite可以做分布式缓存系统并且提供集群节点发现功能,且能够支持通过SQL查询缓存数据即可。
如果大家想要深入了解Ignite这个强大的组件。请访问其官网:https://ignite.apache.org/
2、Ignite构建控制台集群
由于Ignite提供集群发现功能,且能够支持基于内存的分布式缓存(Ignite的分布式缓存支持持久化,只是我们这里没有使用)。因此就可以用Ignite来构建控制台集群,同时将所有控制台需要临时数据都存到分布式集群中即可。整体架构图如下:
核心实现如下:
利用Ignite构建分布式内存集群,所有数据都存到内存集群中
在控制台集群前部署负载均衡,让浏览器通过负载均衡随机访问到控制台机器。
3、拉取客户端流量数据方案
熟悉Sentinel原生控制台原理的同学应该都知道,控制台上可以展示客户端的资源限流详情,如下图。而这个流量信息是控制台定时通过Http接口从客户端拉取过来的。当我们将控制台修改成集群之后,默认所有机器都会去拉取流量数据并存到Ignite分布式缓存中,这样就会导致流量数据不准。因此我们需要选举一台机器出来复制拉取数据。我们的方案是:利用Ignite自带的简单选举功能实现选举(官方文档:https://apacheignite.readme.io/docs/leader-election
)。通过这个功能,我们可以得到当前集群中最新或者最老的节点作为Leader。有了这个功能,我们能轻松的从集群中选举出Leader。
4、客户端心跳方案
由于默认的客户端心跳会发送到一个控制台。并且不会处理控制台失联宕机等场景。因此我们需要修改客户端的心跳发送逻辑,具体方案是采用随机选择控制台的方案。即随机选择配置控制台集群中的一台发送心跳,如果发送失败则自动选择下一台控制台机器。
5、控制台集群登录方案
默认控制台登录通过jsessionid实现,并将登录信息存储到session中。在集群方案中,我们使用UUID为用户生成一个唯一id作为其用户ID,并存储到cookie中。同时将其登录信息存储到分布式缓存集群中。这样每次访问只需要从cookie中获取用户ID,然后根据ID从分布式缓存集群获取到登录信息即可。
三、具体实现与改造
1、Ignite集群实现
基于Ignite分布式缓存,我们主要设计了三个缓存:
Machine:用于存储客户端机器信息。每次客户端发送心跳之后就会更新该缓存。
Metrics:用户存储客户端的流量信息。当Leader拉取到客户端的流量信息之后,就存储到该缓存中。缓存有效时间5分钟(因为控制台默认只展示过去5分钟的流量数据)。
Authority:用户登录信息,用户登录之后就存储到该缓存中。每次校验就从缓存中获取数据。缓存有效时间10分钟(10分钟后重新登录)。
2、登录实现
前面已经介绍过基本实现,本身实现也比较简单。这里直接贴代码吧。
登录成功后,创建cookie,并写入用户信息到缓存:
访问时从缓存中拉取数据判断用户登录状态是否有效:
3、选举实现
前面原理篇我们已经介绍过了,选举是通过获取集群中最新或者最老的节点作为Leader实现的。我的方案中使用最老的节点,这样可以防止在启动的过程中Leader不断变化。
那我们只需要在Ignite节点启动时候判断一下自己是否为Leader(最老节点)就OK了么?当然不是。因为如果Leader节点宕机了,集群中其他节点还必须要选举出一个新的Leader来。因此我们需要对集群中的节点进行监听。好在Ignite本身提供了事件机制。其可以监听Ignite内部的各类事件,如:节点变更,缓存变更,任务变更等等。所以我们可以利用其节点变更来得到当前节点Leader失联的消息即可。
该方案的选举过程和普通的选举过程有些区别。普通的选举是判断Leader节点挂了,自己启动选举流程。而Ignite的选举是判断到某个节点挂了,然后判断一下自己是否变成了最老的节点,如果是,则标记自己为Leader节点。下面是节点收到节点变更事件后的选举机制核心代码:
当机器变成Leader之后,其需要做如下两件事情:
a、不停从客户端机器拉取流量数据
b、将控制台集群所有地址通知给客户端。我们在实际项目中使用的统一配置中心通知。而开源代码中则没有提供具体实现。如有需要可以自行实现,具体实现请查看类:DashboardNodeRegister。
如下是为大家预留的控制台地址注册逻辑,如果需要实现,可修改该代码逻辑即可。
4、规则存储实现
默认的控制台中,每次查询到规则信息都存储在内存中由InMemoryRuleRepositoryAdapter实现。而在集群方案中,我们则将所有的规则数据存储到对应的缓存(流控,降级,系统等)中。为此我们重写了InMemoryRuleRepositoryAdapter,实现了其所有接口方法,让所有的操作都基于分布式缓存实现。该实现类为IgniteRuleRepositoryAdapter。同时每个规则都继承IgniteRuleRepositoryAdapter。结构关系如下图:
四、开源信息
1、开源地址
https://github.com/lifeofcoder/AutoLimiter
2、如何启动
集群控制台只需要直接打包部署autolimiter-dashboard-cluster工程即可。
application.properties文件中只需要修改配置ignite.register.addresses。填上控制台集群中所有机器的IP即可。其他配置和Sentinel的默认控制台完全一样。
默认的用户名和密码为:sentinel/ sentinel。
3、登录界面
由于没有对页面和功能做任何修改,因此其界面和原生的Sentinel控制台界面没有差别。
五、惯例
如果你对本文有任何疑问或者高见,欢迎添加公众号共同交流探讨(添加公众号可以获得”Java高级架构“上10G的视频和图文资料哦)。