文章目录
前言
假设负载后台2个实例
- 192.1.1.101:80
- 192.1.1.102:80【失败】
如果没有监控检查,则会192.1.1.101:80
成功,192.1.1.102:80
失败【等到超时才能感知】,192.1.1.101:80
成功,192.1.1.102:80
失败【等到超时才能感知】。。。。
这样很浪费资源,我们想达到能动态感知后台失效的实例,并剔除掉无效实例,达到一直请求192.1.1.101:80
成功,当192.1.1.102:80
恢复正常后再把正常实例加进来,192.1.1.101:80
成功,192.1.1.102:80
成功。。。
来看下Ribbon是如何实现这个实用的功能。
正常监控检查原理
典型的主动检查需要客户端至少有一个 ping thread,定期检测服务器是否健康,流程如下图:
- Ping thread 检测到两个服务器都正常工作
- 于是客户端会将请求分发到两个服务器上
- 此时 Server 2 宕机,发送到该机器的请求处理失败,返回错误
- 由于新一轮的检测还没有开始,客户端没有意识到服务器已宕机,依旧将请求分发给 Server 2
- 新一轮 Ping 检测到 Server 2 宕机,将其标记为不可用
- 于是客户端将所有请求分发到 Server 1
- 此时 Server 2 恢复正常,但由于还没有被客户端检测到,不会向它分发请求
- 新一轮 Ping 检测到 Server 2 恢复,将其标记为可用
- 客户端重新将请求分配给 Server 2
Ribbon原理
ribbon负载均衡是会从ServerList获取Server对象,其中有个属性isAliveFlag
public class Server {
// 标记是否这台机器是否是活着的 默认值是false
private volatile boolean isAliveFlag;
留有扩展接口IPing以检查其是否活动的接口,类似于心跳检测。
public interface IPing {
// 检查给定的Server是否为“活动的”,这为在负载平衡时选出一个可用的候选Server
public boolean isAlive(Server server);
}
它内部有很多实现如下:
-
PingConstant:永远返回一个bool常量:true or false。
-
NoOpPing:永远返回true
-
AbstractLoadBalancerPing:和
LoadBalancer
有关的一种实现,用于探测服务器节点的适用性。 -
DummyPing:它是
AbstractLoadBalancerPing
的一个空实现- 它是默认的ping实现,
Spring Cloud
默认也是使用的它作为默认实现 - 在
ribbon-eureka
模块下有NIWSDiscoveryPing
这个实现,它基于服务注册中心来判断服务的健康状态
- 它是默认的ping实现,
-
PingUrl:它使用发送真实的Http请求的方式来做健康检查,若返回的状态码是200就证明能够ping通,返回true
如何设置失效实例,以及动态剔除呢?
IPing.isAlive
接口调用的地方如下:
唯一被BaseLoadBalancer中的SerialPingStrategy.pingServers
IPingStrategy的默认实现串行执行ping操作,如果您的IPing实现速度慢或服务器数量众多,则可能不希望这样做。
private static class SerialPingStrategy implements IPingStrategy {
@Override
public boolean[] pingServers(IPing ping, Server[] servers) {
for (int i = 0; i < numCandidates; i++) {
results[i] = false; /* Default answer is DEAD. */
try {
if (ping != null) {
results[i] = ping.isAlive(servers[i]);
}
SerialPingStrategy.pingServers
被调用如下:
BaseLoadBalancer.Pinger
class Pinger {
...
public void runPinger() throws Exception {
boolean[] results = null;
...
results = pingerStrategy.pingServers(ping, allServers);
...
// 这里就是核心:只有ping后是活着的,就会把这个机器添加到up列表里
boolean isAlive = results[i];
if (isAlive) {
newUpList.add(svr);
}
...
}
...
}
这就是isAlive()
方法的作用:true -> 表示该机器是up的,从而得到新的up列表就是最新的可用的机器列表了。
BaseLoadBalancer.Pinger
又是由Timer定时调用
// 任务Task
class PingTask extends TimerTask {
@Override
public void run() {
new Pinger(pingStrategy).runPinger();
}
}
// 这里是它的PingTask的唯一调用处
void setupPingTask() {
...
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
...
}
IPing#isAlive()
方法是由Timer定时调用的,pingIntervalSeconds
默认值是30s,也就说30s会去心跳一次Server,看它活着与否。当然你可以通过key:NFLoadBalancerPingInterval
自己配置(单位是秒)
拓展
相关自动化配置类RibbonClientConfiguration
-
IClientConfig - 默认:DefaultClientConfigImpl
-
IRule- 默认:ZoneAvoidanceRule
-
IPing- 默认:DummyPing
-
ServerList - 默认:ConfigurationBasedServerList
- 默认从配置文件
listOfServers
读取
- 默认从配置文件
-
ServerListUpdater - 默认就是:PollingServerListUpdater DynamicServerListLoadBalancer的策略可用于执行动态服务器列表更新的不同方式。
- 定时默认每30s重新拉取最新的ServerList,如果是默认的则可以从配置文件读取最新的配置
listOfServers
- 定时默认每30s重新拉取最新的ServerList,如果是默认的则可以从配置文件读取最新的配置
-
ILoadBalancer - 默认:ZoneAwareLoadBalancer
-
ServerListFilter - 默认:ZonePreferenceServerListFilter
-
RibbonLoadBalancerContext
-
RetryHandler
-
ServerIntrospector - 默认:DefaultServerIntrospector
自定义监控检查
自定义配置
先自定义2个配置
- LakerPingAppendString:监控检查url后缀 例如:http://10.3.1.121:89/health,这里即为
/health
- LakerIsSecure: 是http还是https
读取配置值用
pingAppendString = clientConfig.getPropertyAsString(
new CommonClientConfigKey<Boolean>("LakerPingAppendString") {
},
"");
isSecure = clientConfig.getPropertyAsBoolean(
new CommonClientConfigKey<Boolean>("LakerIsSecure") {
},
false);
自定义HealthCheck
自定义HealthCheck继承AbstractLoadBalancerPing接口
@Slf4j
public class HealthCheck extends AbstractLoadBalancerPing {
String pingAppendString = "";
boolean isSecure = false;
@Override
public boolean isAlive(Server server) {
... check
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
... 属性初始化
}
}
配置文件
roadnet-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule # 配置负载均衡算法
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList # 配不配都行默认也是这个
NFLoadBalancerPingClassName: com.laker.zuul.ext.zuul.ping.HealthCheck # 配置我们的自定义监控检查
LakerPingAppendString: /abc
LakerIsSecure: false
NFLoadBalancerPingInterval: 10 # 默认是30s
listOfServers: http://10.7.11.13:9006,http://localhost:8081
ConnectTimeout: 1000
ReadTimeout: 3000
MaxTotalHttpConnections: 500
MaxConnectionsPerHost: 100
MaxAutoRetries: 1
MaxAutoRetriesNextServer: 1
参考:
-
cloud.tencent.com/developer/a…
加群一起抱团取暖,共同进步
🍎QQ群【837324215】
🍎关注我的公众号【Java大厂面试官】,一起学习呗🍎🍎🍎
🍎个人vx【lakernote】