维护服务实例清单
服务实例清单的维护主要在抽象类BaseLoadBalancer中实现。在BaseLoadBalancer类中主要实现了:
1、维护了“up”状态的服务列表和全部的服务实例列表
2、通过IPing实现了设置服务实例列表的状态
服务实例清单
@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> allServerList = Collections
.synchronizedList(new ArrayList<Server>());
@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> upServerList = Collections
.synchronizedList(new ArrayList<Server>());
增加服务实例
在BaseLoadBalancer类中,有三个重载方法,如下所示:
@Override
public void addServers(List<Server> newServers) {
if (newServers != null && newServers.size() > 0) {
try {
ArrayList<Server> newList = new ArrayList<Server>();
newList.addAll(allServerList);
newList.addAll(newServers);
setServersList(newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Exception while adding Servers", name, e);
}
}
}
void addServers(Object[] newServers) {
if ((newServers != null) && (newServers.length > 0)) {
try {
ArrayList<Server> newList = new ArrayList<Server>();
newList.addAll(allServerList);
for (Object server : newServers) {
if (server != null) {
if (server instanceof String) {
server = new Server((String) server);
}
if (server instanceof Server) {
newList.add((Server) server);
}
}
}
setServersList(newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Exception while adding Servers", name, e);
}
}
}
public void addServer(Server newServer) {
if (newServer != null) {
try {
ArrayList<Server> newList = new ArrayList<Server>();
newList.addAll(allServerList);
newList.add(newServer);
setServersList(newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error adding newServer {}", name, newServer.getHost(), e);
}
}
}
前面的三个重载方法,最终都是通过setServersList()方法实现为all和up两个列表进行赋值。
public void setServersList(List lsrv) {
//获取写锁
Lock writeLock = allServerLock.writeLock();
logger.debug("LoadBalancer [{}]: clearing server list (SET op)", name);
//定义列表集合
ArrayList<Server> newServers = new ArrayList<Server>();
//加锁
writeLock.lock();
try {
ArrayList<Server> allServers = new ArrayList<Server>();
for (Object server : lsrv) {
if (server == null) {//处理null的情况
continue;
}
if (server instanceof String) {//如果存储的是service Id,则创建对应的server对象
server = new Server((String) server);
}
if (server instanceof Server) {//添加到allServers变量,即把所有服务实例转换成server对象,并存储到了局部变量allServers中
logger.debug("LoadBalancer [{}]: addServer [{}]", name, ((Server) server).getId());
allServers.add((Server) server);
} else {
throw new IllegalArgumentException(
"Type String or Server expected, instead found:"
+ server.getClass());
}
}
//判断服务实例清单,是否发生变化,如果发送变化,则触发对应的监听方法。
boolean listChanged = false;
if (!allServerList.equals(allServers)) {
listChanged = true;
if (changeListeners != null && changeListeners.size() > 0) {
List<Server> oldList = ImmutableList.copyOf(allServerList);
List<Server> newList = ImmutableList.copyOf(allServers);
for (ServerListChangeListener l: changeListeners) {
try {
l.serverListChanged(oldList, newList);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error invoking server list change listener", name, e);
}
}
}
}
if (isEnablePrimingConnections()) {
for (Server server : allServers) {
if (!allServerList.contains(server)) {
server.setReadyToServe(false);
newServers.add((Server) server);
}
}
if (primeConnections != null) {
primeConnections.primeConnectionsAsync(newServers, this);
}
}
// 忽略之前的设置,重新初始化upServerList 变量
allServerList = allServers;
if (canSkipPing()) {
for (Server s : allServerList) {
s.setAlive(true);
}
upServerList = allServerList;
} else if (listChanged) {//如果发送了变化,则调用forceQuickPing()进行验证服务实例清单
forceQuickPing();
}
} finally {
writeLock.unlock();
}
}
public void forceQuickPing() {
if (canSkipPing()) {
return;
}
logger.debug("LoadBalancer [{}]: forceQuickPing invoking", name);
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error running forceQuickPing()", name, e);
}
}
内部类Pinger中最终通过pingerStrategy.pingServers()方法实现了服务实例可用性验证,通过验证结果,重新设置upServerList列表。
class Pinger {
private final IPingStrategy pingerStrategy;
public Pinger(IPingStrategy pingerStrategy) {
this.pingerStrategy = pingerStrategy;
}
public void runPinger() throws Exception {
if (!pingInProgress.compareAndSet(false, true)) {
return; // Ping in progress - nothing to do
}
// we are "in" - we get to Ping
Server[] allServers = null;
boolean[] results = null;
Lock allLock = null;
Lock upLock = null;
try {
/*
* The readLock should be free unless an addServer operation is
* going on...
*/
allLock = allServerLock.readLock();
allLock.lock();
allServers = allServerList.toArray(new Server[allServerList.size()]);
allLock.unlock();
int numCandidates = allServers.length;
//内部类SerialPingStrategy
results = pingerStrategy.pingServers(ping, allServers);
final List<Server> newUpList = new ArrayList<Server>();
final List<Server> changedServers = new ArrayList<Server>();
for (int i = 0; i < numCandidates; i++) {
boolean isAlive = results[i];
Server svr = allServers[i];
boolean oldIsAlive = svr.isAlive();
svr.setAlive(isAlive);
if (oldIsAlive != isAlive) {
changedServers.add(svr);
logger.debug("LoadBalancer [{}]: Server [{}] status changed to {}",
name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
}
if (isAlive) {
newUpList.add(svr);
}
}
upLock = upServerLock.writeLock();
upLock.lock();
upServerList = newUpList;
upLock.unlock();
notifyServerStatusChangeListener(changedServers);
} finally {
pingInProgress.set(false);
}
}
}
内部类SerialPingStrategy :
private static class SerialPingStrategy implements IPingStrategy {
@Override
public boolean[] pingServers(IPing ping, Server[] servers) {
int numCandidates = servers.length;
boolean[] results = new boolean[numCandidates];
logger.debug("LoadBalancer: PingTask executing [{}] servers configured", numCandidates);
for (int i = 0; i < numCandidates; i++) {
results[i] = false; /* Default answer is DEAD. */
try {
if (ping != null) {
results[i] = ping.isAlive(servers[i]);
}
} catch (Exception e) {
logger.error("Exception while pinging Server: '{}'", servers[i], e);
}
}
return results;
}
}
服务实例下线
public void markServerDown(Server server) {
if (server == null || !server.isAlive()) {
return;
}
logger.error("LoadBalancer [{}]: markServerDown called on [{}]", name, server.getId());
server.setAlive(false);
// forceQuickPing();
notifyServerStatusChangeListener(singleton(server));
}
触发下线通知。
private void notifyServerStatusChangeListener(final Collection<Server> changedServers) {
if (changedServers != null && !changedServers.isEmpty() && !serverStatusListeners.isEmpty()) {
for (ServerStatusChangeListener listener : serverStatusListeners) {
try {
listener.serverStatusChanged(changedServers);
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error invoking server status change listener", name, e);
}
}
}
}
服务实例清单 对象
public interface ServerList<T extends Server> {
//获取初始化服务实例清单
public List<T> getInitialListOfServers();
//获取更新后的服务实例清单
public List<T> getUpdatedListOfServers();
}
默认配置
//EurekaRibbonClientConfiguration.java
@Configuration(proxyBeanMethods = false)
public class EurekaRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config,
Provider<EurekaClient> eurekaClientProvider) {
//如果自定义了,则使用自定义的
if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
return this.propertiesFactory.get(ServerList.class, config, serviceId);
}
//默认采用DiscoveryEnabledNIWSServerList 作为服务实例清单
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
config, eurekaClientProvider);
DomainExtractingServerList serverList = new DomainExtractingServerList(
discoveryServerList, config, this.approximateZoneFromHostname);
return serverList;
}
}
获取服务实例清单
DiscoveryEnabledNIWSServerList 类的核心方法,通过EurekaClient 对象,实现与EurekaServer交互,进而来获取服务实例清单。
@Override
public List<DiscoveryEnabledServer> getInitialListOfServers(){
return obtainServersViaDiscovery();
}
@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
return obtainServersViaDiscovery();
}
private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();
if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {
logger.warn("EurekaClient has not been initialized yet, returning an empty list");
return new ArrayList<DiscoveryEnabledServer>();
}
//获取服务实例客户端
EurekaClient eurekaClient = eurekaClientProvider.get();
if (vipAddresses!=null){
for (String vipAddress : vipAddresses.split(",")) {//遍历应用集合
// 使用eurekaClient 获取对应的InstanceInfo集合
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
//遍历InstanceInfo实例集合
for (InstanceInfo ii : listOfInstanceInfo) {
//获取up状态的服务实例
if (ii.getStatus().equals(InstanceStatus.UP)) {
//是否启用重写接口,默认不启用
if(shouldUseOverridePort){
if(logger.isDebugEnabled()){
logger.debug("Overriding port on client name: " + clientName + " to " + overridePort);
}
//使用重写接口设置服务实例的接口
InstanceInfo copy = new InstanceInfo(ii);
if(isSecure){
ii = new InstanceInfo.Builder(copy).setSecurePort(overridePort).build();
}else{
ii = new InstanceInfo.Builder(copy).setPort(overridePort).build();
}
}
//创建DiscoveryEnabledServer 对象,并添加到serverList集合
DiscoveryEnabledServer des = createServer(ii, isSecure, shouldUseIpAddr);
serverList.add(des);
}
}
if (serverList.size()>0 && prioritizeVipAddressBasedServers){
break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
}
}
}
return serverList;
}
//创建DiscoveryEnabledServer 对象
protected DiscoveryEnabledServer createServer(final InstanceInfo instanceInfo, boolean useSecurePort, boolean useIpAddr) {
DiscoveryEnabledServer server = new DiscoveryEnabledServer(instanceInfo, useSecurePort, useIpAddr);
// Get availabilty zone for this instance.
EurekaClientConfig clientConfig = eurekaClientProvider.get().getEurekaClientConfig();
String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
String instanceZone = InstanceInfo.getZone(availZones, instanceInfo);
server.setZone(instanceZone);
return server;
}
服务实例清单 更新器-ServerListUpdater
任何实例都可能存在开发者主动的上下线行为,或者一些网络故障以及自身硬件的意外导致实例处于不同的状态,所以服务实例清单也是处于一个不断变化的过程。为了维护这个清单,Netflix提供了ServerListUpdater接口,通过它可以及时更新服务实例清单。而有两个ServerListUpdater接口的实现类,分别是EurekaNotificationServerListUpdater和PollingServerListUpdater。
public interface ServerListUpdater {
public interface UpdateAction {
void doUpdate();
}
void start(UpdateAction updateAction);
void stop();
String getLastUpdate();
long getDurationSinceLastUpdateMs();
int getNumberMissedCycles();
int getCoreThreads();
}
默认配置
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return new PollingServerListUpdater(config);
}
}
start()方法
@Override
public synchronized void start(final UpdateAction updateAction) {
if (isActive.compareAndSet(false, true)) {//当前实例存活状态是否改变
final Runnable wrapperRunnable = new Runnable() {//新建子线程
@Override
public void run() {
if (!isActive.get()) {//不存活,则退出线程任务
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
return;
}
try {//如果存活,更新
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
} catch (Exception e) {
logger.warn("Failed one update cycle", e);
}
}
};
//线程任务
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,//线程任务
initialDelayMs,//延时执行时间,默认1s
refreshIntervalMs,//刷新时间间隔,默认30s
TimeUnit.MILLISECONDS//单位毫秒
);
} else {
logger.info("Already active, no-op");
}
}
服务实例心跳检测
通过一个定时任务来ping服务器,这个任务是每隔pingIntervalSeconds秒执行一次,这样Eureka的客户端就可以得到最新服务的状态了。而这里的定时任务的执行内容是通过PingTask类来实现的
//BaseLoadBalancer.java
void setupPingTask() {
if (canSkipPing()) {
return;
}
if (lbTimer != null) {
lbTimer.cancel();
}
lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
true);
lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
//初始化时执行一次
forceQuickPing();
}
而PingTask 中又通过内部类Pinger的runPinger()方法实现。
class PingTask extends TimerTask {
public void run() {
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error pinging", name, e);
}
}
}
public void forceQuickPing() {
if (canSkipPing()) {
return;
}
logger.debug("LoadBalancer [{}]: forceQuickPing invoking", name);
try {
new Pinger(pingStrategy).runPinger();
} catch (Exception e) {
logger.error("LoadBalancer [{}]: Error running forceQuickPing()", name, e);
}
}
IPing接口
具体执行心跳监测服务实例的是IPing接口。
public interface IPing {
public boolean isAlive(Server server);
}
在Spring Boot工程中,如果没有配置Eureka服务发现的客户端,则使用DummyPing,它将恒定返回true。如果配置了Eureka服务发现的客户端,则使用NIWSDiscoveryPing,它将通过和Eureka服务治理中心通信的机制来判定。其他的实现,NoOpPing是恒定返回true;PingConstant是设置一个布尔值(boolean),让isAlive方法恒定返回这个布尔值;PingUrl是通过配置具体的url请求来断定服务是否可用。
NIWSDiscoveryPing
public class NIWSDiscoveryPing extends AbstractLoadBalancerPing {
BaseLoadBalancer lb = null;
public NIWSDiscoveryPing() {
}
public BaseLoadBalancer getLb() {
return lb;
}
public void setLb(BaseLoadBalancer lb) {
this.lb = lb;
}
public boolean isAlive(Server server) {
boolean isAlive = true;
//如果断定服务实例是Eureka服务发现的实例,就采用服务实例的状态判断是否可用。
if (server!=null && server instanceof DiscoveryEnabledServer){
DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server;
InstanceInfo instanceInfo = dServer.getInstanceInfo();
if (instanceInfo!=null){
InstanceStatus status = instanceInfo.getStatus();
if (status!=null){
isAlive = status.equals(InstanceStatus.UP);
}
}
}
return isAlive;
}
@Override
public void initWithNiwsConfig(
IClientConfig clientConfig) {
}
}
全局配置 OR 单独配置
全局配置是指在服务消费者配置的内容对所有服务提供者有效。单独配置是指服务消费者可以配置对某个服务提供者有效,而对其他服务提供者无效。
局部定义
- <clientName>.ribbon.NFLoadBalancerClassName:负载均衡类,需实现。ILoadBalancer接口。
- <clientName>.ribbon.NFLoadBalancerRuleClassName:负载均衡策略,需实现IRule接口。
- <clientName>.ribbon.NFLoadBalancerPingClassName:心跳监测类,需实现IPing接口。
- <clientName>.ribbon.NIWSServerListClassName:服务实例清单类,需实现ServerList接口。
- <clientName>.ribbon.NIWSServerListFilterClassName:服务实例清单过滤类,需实现ServerListFilter接口。
Spring Cloud还提供了@RibbonClient和@RibbonClients。针对单个微服务配置类使用@RibbonClient,针对多个微服务配置使用@RibbonClients。