Eureka源碼核心類預備知識
1. 前言
1.1 Eureka的異地多活
異地多活一般是指在不同城市建立獨立的數(shù)據(jù)中心。
活是相對于主備關系中的熱備而言的。熱備是指備份機房隨時全量備份著主機房中的數(shù)據(jù),但平時不 支撐業(yè)務需求,即不對外提供服務。只有在主機房出現(xiàn)故障時才會切換到備份機房,由備份機房對外提 供服務。也就是說,平時只有主機房是活的。
多活則是指這些機房間屬于主從關系,即這些機房平時都支撐業(yè)務需求,都對外提供服務,相互備 份。
1.2 Region和Zone

Eureka中具有Region與Availability Zone(簡稱AZ)概念,都是云計算中的概念。
為了方便不同地理區(qū)域中用戶的使用,大型云服務提供商一般會根據(jù)用戶需求量在不同的城市、省份、 國家或洲創(chuàng)建不同的大型云計算機房。這些不同區(qū)域機房間一般是不能“內(nèi)網(wǎng)連通”的。這些區(qū)域就稱為 一個Region。
這里存在一個問題:同一Region機房是如何實現(xiàn)同域容災的?為了增強容災能力,在一個Region中又 設置了不同的Availability Zone。這些AZ間實現(xiàn)了內(nèi)網(wǎng)連通,且用戶可以根據(jù)自己所在的具體的位置自動 選擇同域中的不同AZ。當用戶所要訪問的AZ出現(xiàn)問題后,系統(tǒng)會自動切換到其它可用的AZ。
例如,AWS將全球劃分為了很多的Region,例如美國東部區(qū)、美國西部區(qū)、歐洲區(qū)、非洲開普敦區(qū)、 亞太區(qū)等。像Eureka系統(tǒng)架構圖中的us-east-1c、us-east-1d、us-east-1e就是us-east-1這個Region中 的c、d、e三個AZ。
再如,阿里云在我國境內(nèi)的Region有杭州、北京、深圳、青島、香港等,境外Region有亞太東南1區(qū) (新加坡)、亞太東南2區(qū)(悉尼)、亞太東北1區(qū)(東京)等
1.3 Region和AZ需求

假設某公司的服務器有Beijing、Shanghai等多個Region。Beijing這個Region中存在兩個AZ,分別是 bj-1與bj-2,每個AZ中有三臺Eureka Server。
h-1與h-2兩臺主機提供的都是相同的Service服務,根據(jù)地理位置的不同,這兩臺主機分別注冊到了距 離自己最近的不同AZ的Eureka Server。
2.核心類
2.1 客戶端核心類
2.1.1 InstanceInfo-實例信息類
// 客戶端中,表示自身實例信息
// 服務端中,表示實例存在服務端注冊表中的信息
public class InstanceInfo {
// ......
// 客戶端中,表示自己的真實工作狀態(tài)
// 服務端中,表示服務發(fā)現(xiàn)時實例想要暴露給其他實例的工作狀態(tài),不一定是實例的真實工作狀態(tài)
private volatile InstanceStatus status = InstanceStatus.UP;
// 覆蓋狀態(tài),服務端可以根據(jù)一定規(guī)則匹配出 status
// 外界修改實例在服務端中狀態(tài)(比如通過 actuator 修改狀態(tài))就是修改覆蓋狀態(tài)
private volatile InstanceStatus overriddenStatus = InstanceStatus.UNKNOWN;
// 判斷實例信息在服務端中是否是臟的
private volatile boolean isInstanceInfoDirty = false;
// 租約信息
private volatile LeaseInfo leaseInfo;
// 記錄實例信息在服務端最近一次修改的時間
private volatile Long lastUpdatedTimestamp;
// 記錄實例信息在客戶端最近一次修改的時間
private volatile Long lastDirtyTimestamp;
// ......
}
- InstanceStatus-實例狀態(tài)類
public enum InstanceStatus {
UP, // 啟動狀態(tài),表示實例對外正常提供服務
DOWN, // 下線狀態(tài),實例健康檢查失敗時修改為該狀態(tài)
STARTING, // 啟動中狀態(tài),表示實例正在初始化啟動中
OUT_OF_SERVICE, // 停止服務狀態(tài),表示實例不對外提供服務
UNKNOWN; // 未知狀態(tài)
// ......
}
- LeaseInfo 租約信息類
public class LeaseInfo {
public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
public static final int DEFAULT_LEASE_DURATION = 90;
// 客戶端維護的心跳間隔時間
private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
// 客戶端維護的租約持續(xù)時間
private int durationInSecs = DEFAULT_LEASE_DURATION;
// 服務端維護的實例注冊時間
private long registrationTimestamp;
// 服務端維護的實例最近一次更新時間
private long lastRenewalTimestamp;
// 服務端維護的實例過期清理時間
private long evictionTimestamp;
// 服務端維護的實例啟動時間
private long serviceUpTimestamp;
// ......
}
2.1.2 Application
一個Application實例保存著一個特定微服務的所有提供者實例
public class Application {
private static Random shuffleRandom = new Random();
private String name;
@XStreamOmitField
private volatile boolean isDirty = false;
/**
* 保存著當前name所指定的微服務名稱的所有InstanceInfo 實例
*/
@XStreamImplicit
private final Set<InstanceInfo> instances;
private final AtomicReference<List<InstanceInfo>> shuffledInstances;
// key:instanceId value:InstanceInfo實例
private final Map<String, InstanceInfo> instancesMap;
2.1.3 Applications
該類封裝了來自于Eureka Server的所有注冊信息,我們可成為 "客戶端注冊表"
public class Applications {
private static class VipIndexSupport {
final AbstractQueue<InstanceInfo> instances = new ConcurrentLinkedQueue<>();
final AtomicLong roundRobinIndex = new AtomicLong(0);
final AtomicReference<List<InstanceInfo>> vipList = new AtomicReference<List<InstanceInfo>>(Collections.emptyList());
public AtomicLong getRoundRobinIndex() {
return roundRobinIndex;
}
public AtomicReference<List<InstanceInfo>> getVipList() {
return vipList;
}
}
private static final String STATUS_DELIMITER = "_";
private String appsHashCode;
private Long versionDelta;
@XStreamImplicit
private final AbstractQueue<Application> applications;
// key:微服務名稱 value:Application實例
private final Map<String, Application> appNameApplicationMap;
private final Map<String, VipIndexSupport> virtualHostNameAppMap;
private final Map<String, VipIndexSupport> secureVirtualHostNameAppMap;
}
2.2 服務端
2.2.1 AbstractInstanceRegistry
服務端具體處理客戶端請求(心跳續(xù)租、注冊、變更狀態(tài)等等)的類
public abstract class AbstractInstanceRegistry implements InstanceRegistry {
// ......
// 服務實例租約信息
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
// 覆蓋狀態(tài) map
protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
.newBuilder().initialCapacity(500)
.expireAfterAccess(1, TimeUnit.HOURS)
.<String, InstanceStatus>build().asMap();
// 最近注冊隊列,實例注冊到服務端時添加
// 先進先出隊列,滿1000時移除最先添加的
private final CircularQueue<Pair<Long, String>> recentRegisteredQueue;
// 最近下架隊列,實例從服務端下架時添加
// 先進先出隊列,滿1000時移除最先添加的
private final CircularQueue<Pair<Long, String>> recentCanceledQueue;
// 最近變更隊列
// 有定時任務維護的隊列,每30s執(zhí)行一次,移除添加進該隊列超過3分鐘的實例變更信息
private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 讀鎖(處理客戶端注冊、下架、狀態(tài)變更、刪除狀態(tài)時使用)
private final Lock read = readWriteLock.readLock();
// 寫鎖(處理客戶端拉取增量注冊表時使用)
private final Lock write = readWriteLock.writeLock();
// 服務端統(tǒng)計最近一分鐘預期收到客戶端實例心跳續(xù)租的請求數(shù)
protected volatile int numberOfRenewsPerMinThreshold;
// 服務端統(tǒng)計預期收到心跳續(xù)租的客戶端實例數(shù)
protected volatile int expectedNumberOfClientsSendingRenews;
// 響應緩存
// 服務端處理客戶端拉取注冊表請求時使用
protected volatile ResponseCache responseCache;
// ......
// 處理注冊
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {...}
// 處理下架
public boolean cancel(String appName, String id, boolean isReplication) {...}
// 具體下架處理
protected boolean internalCancel(String appName, String id, boolean isReplication) {...}
// 處理心跳續(xù)租
public boolean renew(String appName, String id, boolean isReplication) {...}
// 處理變更狀態(tài)
public boolean statusUpdate(String appName, String id,
InstanceStatus newStatus, String lastDirtyTimestamp,
boolean isReplication) {...}
// 處理刪除狀態(tài)
public boolean deleteStatusOverride(String appName, String id,
InstanceStatus newStatus,
String lastDirtyTimestamp,
boolean isReplication) {...}
// 處理實例過期清理
public void evict(long additionalLeaseMs) {...}
// 處理拉取全量注冊表(本地全量注冊表 + 可能包含全部遠程 region 注冊表)
public Application getApplication(String appName, boolean includeRemoteRegion) {...}
// 處理拉取全量注冊表(本地全量注冊表 + 可能包含指定遠程 region 全量注冊表)
public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {...}
// 處理拉取增量注冊表(本地增量注冊表 + 可能包含指定遠程 region 增量注冊表)
public Applications getApplicationDeltasFromMultipleRegions(String[] remoteRegions) {...}
......
}
- Lease-只有服務端維護的實例租約信息類
public class Lease<T> {
// 實例下架時間
private long evictionTimestamp;
// 實例注冊時間
private long registrationTimestamp;
// 實例啟動時間
private long serviceUpTimestamp;
// 實例租約過期時間
private volatile long lastUpdateTimestamp;
......
}
- ResponseCacheImpl:響應緩存實現(xiàn)類
public class ResponseCacheImpl implements ResponseCache {
// ......
// 只讀緩存
private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
// 讀寫緩存
// LoadingCache:Guava 提供的本地緩存,多線程的場景下保證只有一個線程加載相應緩存項
private final LoadingCache<Key, Value> readWriteCacheMap;
// 判斷是否使用只讀緩存
private final boolean shouldUseReadOnlyResponseCache;
// ......
}
2.2.2 PeerAwareInstanceRegistryImpl
處理集群節(jié)點間相關操作的實現(xiàn)類
public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {
// 當前服務端節(jié)點的啟動時間
private long startupTime = 0;
// 判斷服務端啟動時同步集群節(jié)點注冊表的實例數(shù)是否為空
private boolean peerInstancesTransferEmptyOnStartup = true;
// 最近一分鐘同步復制給集群節(jié)點的次數(shù)
private final MeasuredRate numberOfReplicationsLastMin;
// 服務端的相鄰集群節(jié)點,配置文件中配置
protected volatile PeerEurekaNodes peerEurekaNodes;
}
3. Jersey通信框架
Spring Cloud中Eureka Client與Eureka Server的通信,及Eureka Server間的通信,均采用的是Jersey框架。
Jersey框架是一個開源的RESTful框架,實現(xiàn)了JAX-RS規(guī)范。該框架的作用與SpringMVC是相同的,其 也是用戶提交URI后,在處理器中進行路由匹配,路由到指定的后臺業(yè)務。這個路由功能同樣也是通過 處理器完成的,只不過這里的處理器不叫Controller,而叫Resource。
@Produces({"application/xml", "application/json"})
public class InstanceResource {
private static final Logger logger = LoggerFactory
.getLogger(InstanceResource.class);
private final PeerAwareInstanceRegistry registry;
private final EurekaServerConfig serverConfig;
private final String id;
private final ApplicationResource app;
InstanceResource(ApplicationResource app, String id, EurekaServerConfig serverConfig, PeerAwareInstanceRegistry registry) {
this.app = app;
this.id = id;
this.serverConfig = serverConfig;
this.registry = registry;
}
...
}
參考文章
springcloud-source-study學習github地址
以上就是Eureka源碼核心類預備知識的詳細內(nèi)容,更多關于Eureka源碼核心類的資料請關注腳本之家其它相關文章!
相關文章
Java字節(jié)與字符流永久存儲json數(shù)據(jù)
本篇文章給大家詳細講述了Java字節(jié)與字符流永久存儲json數(shù)據(jù)的方法,以及代碼分享,有興趣的參考學習下。2018-02-02
使用Postman傳遞arraylist數(shù)據(jù)給springboot方式
這篇文章主要介紹了使用Postman傳遞arraylist數(shù)據(jù)給springboot方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

