源碼 狀態機_[源碼閱讀] 阿里SOFA服務注冊中心MetaServer(1)

[源碼閱讀] 阿里SOFA服務注冊中心MetaServer(1)

  • 0x00 摘要

  • 0x01 服務注冊中心

    • 1.1 服務注冊中心簡介

    • 1.2 SOFARegistry 總體架構

    • 1.3 為什么要分層

  • 0x02 MetaServer

    • 2.1簡介

    • 2.2 問題

  • 0x03 代碼結構

  • 0x04 啟動運行

    • 4.1 集成部署

    • 4.2 獨立部署

  • 0x05 總體邏輯

    • 5.1 程序主體

    • 5.2 配置

  • 0x06 啟動

    • 6.1 架構

    • 6.2 類定義

    • 6.3 通信 Exchange

    • 6.4 啟動入口

    • 6.4.1 啟動服務Server

    • 6.4.2 ExecutorManager

    • 6.4.3 ServiceFactory

  • 0xFF 參考

0x00 摘要

SOFARegistry 是螞蟻金服開源的一個生產級、高時效、高可用的服務注冊中心。本系列將帶領大家一起分析其MetaServer的實現機制,本文為第一篇,介紹MetaServer總體架構。

本系列總體參考了官方的博客,具體請參見"0xFF 參考"。大家可以把參考作為總綱,我這系列文章作為注釋補遺翻閱。

0x01 服務注冊中心

1.1 服務注冊中心簡介

在微服務架構下,一個互聯網應用的服務端背后往往存在大量服務間的相互調用。例如服務 A 在鏈路上依賴于服務 B,那么在業務發生時,服務 A 需要知道服務 B 的地址,才能完成服務調用。而分布式架構下,每個服務往往都是集群部署的,集群中的機器也是經常變化的,所以服務 B 的地址不是固定不變的。如果要保證業務的可靠性,服務調用者則需要感知被調用服務的地址變化。

既然成千上萬的服務調用者都要感知這樣的變化,那這種感知能力便下沉成為微服務中一種固定的架構模式:服務注冊中心

服務注冊中心里,有服務提供者和服務消費者兩種重要的角色,服務調用方是消費者,服務被調方是提供者。對于同一臺機器,往往兼具兩者角色,既被其它服務調用,也調用其它服務。服務提供者將自身提供的服務信息發布到服務注冊中心,服務消費者通過訂閱的方式感知所依賴服務的信息是否發生變化。

服務注冊中心在服務調用的場景中,扮演一個“中介”的角色,服務發布者 (Publisher) 將服務發布到服務注冊中心,服務調用方 (Subscriber) 通過訪問服務注冊中心就能夠獲取到服務信息,進而實現調用。

Subscriber 在第一次調用服務時,會通過 Registry 找到相應的服務的 IP 地址列表,通過負載均衡算法從 IP 列表中取一個服務提供者的服務器調用服務。同時 Subscriber 會將 Publisher 的服務列表數據緩存到本地,供后續使用。當 Subscriber 后續再調用 Publisher 時,優先使用緩存的地址列表,不需要再去請求Registry。

+----------+????????????????????????????????????+---------+
|Subscriber|?+----------+??????????|??????????????|??????????+---------+
??????????????????????|??????????????|
+----------+??????????|??????????????v
|Subscriber|?+----------+????????????????????????????????|???+----------++?????????+---------+
+----------+??????????|??????????????^
|Subscriber|?+----------+?????????????????????????|
?????????????????????????????????????|??????????+---------+
?????????????????????????????????????+----------+Publisher|
????????????????????????????????????????????????+---------+

服務注冊中心Registry的最主要能力是服務注冊和服務發現兩個過程。

  • 服務注冊的過程最重要是對服務發布的信息進行存儲。
  • 服務發現的過程是把服務發布端的所有變化(包括節點變化和服務信息變化)及時準確的通知到訂閱方的過程。

1.2 SOFARegistry 總體架構

1.2.1 分層

SOFARegistry 作為服務注冊中心,分為4個層,分別為:

  • Client 層

Client 層是應用服務器集群。Client 層是應用層,每個應用系統通過依賴注冊中心相關的客戶端 jar 包,通過編程方式來使用服務注冊中心的服務發布和服務訂閱能力。

  • Session 層

Session層是服務器集群。顧名思義,Session 層是會話層,通過長連接和 Client 層的應用服務器保持通訊,負責接收 Client 的服務發布和服務訂閱請求。

在服務注冊中心的服務端因為每個存儲節點對應的客戶端的鏈接數據量有限,必須進行特殊的一層劃分用于專門收斂無限擴充的客戶端連接,然后在透傳相應的請求到存儲層。

該層只在內存中保存各個服務的發布訂閱關系,對于具體的服務信息,只在 Client 層和 Data 層之間透傳轉發。Session 層是一個無數據狀態的代理層,可以隨著 Client 層應用規模的增長而擴容。

因為 SOFARegistry 的服務發現需要較高的時效性,對外表現為主動推送變更到客戶端,所以推送的主體實現也集中在 Session 層,內部的推拉結合主要是通過 Data 存儲層的數據版本變更推送到所有 Session 節點,各個 Session 節點根據存儲的訂閱關系和首次訂閱獲取的數據版本信息進行比對,最終確定推送給那些服務消費方客戶端。

  • Data 層

數據服務器集群。Data 層通過分片存儲的方式保存著所用應用的服務注冊數據。數據按照 dataInfoId(每一份服務數據的唯一標識)進行一致性 Hash 分片,多副本備份,保證數據的高可用。Data 層可以隨著數據規模的增長,在不影響業務的前提下實現平滑的擴縮容。

  • Meta 層

元數據服務器集群。這個集群管轄的范圍是 Session 服務器集群和 Data 服務器集群的服務器信息,其角色就相當于 SOFARegistry 架構內部的服務注冊中心,只不過 SOFARegistry 作為服務注冊中心是服務于廣大應用服務層,而 Meta 集群是服務于 SOFARegistry 內部的 Session 集群和 Data 集群,Meta 層能夠感知到 Session 節點和 Data 節點的變化,并通知集群的其它節點。

1.3 為什么要分層

SOFARegistry 內部為什么要進行數據分層,是因為系統容量的限制。

在 SOFARegistry 的應用場景中,體量龐大的數據主要有兩類:Session 數據、服務信息數據。兩類數據的相同之處在于其數據量都會不斷擴展,而不同的是其擴展的原因并不相同:

  • Session 是對應于 Client 的連接,其數據量是隨著業務機器規模的擴展而增長,
  • 服務信息數據量的增長是由 Publisher 的發布所決定。

所以 SOFARegistry 通過分層設計,將兩種數據隔離,從而使二者的擴容互不影響。

9463fb002ff02a3f4a0c6516cc1c73c8.png
圖3 - 分層,擴容互不影響

這也是 SOFARegistry 設計三層模型的原因,通過 SessionServer 來負責與 Client 的連接,將每個 Client 的連接數收斂到 1,這樣當 Client 數量增長時,只需要擴容 SessionServer 集群就可以了。所以從設計初衷上我們就能夠看出來 SessionServer 必須要滿足的兩個主要能力:從 DataServer 獲取服務信息數據;以及保存與 Client 的會話。

0x02 MetaServer

2.1簡介

MetaServer 在 SOFARegistry 中,承擔著集群元數據管理的角色,用來維護集群成員列表,可以認為是 SOFARegistry 注冊中心的注冊中心。

MetaServer 作為 SOFARegistry 的元數據中心,其核心功能可以概括為:集群成員列表管理。比如:

  • 節點列表的注冊與存儲
  • 節點列表的變更通知
  • 節點健康監測

當 SessionServer 和 DataServer 需要知道集群列表,并且需要擴縮容時,MetaServer 將會提供相應的數據。

其內部架構如下圖所示:

74c5b01fd53a54472aed041f84bf74a1.png
內部架構圖

MetaServer 基于 Bolt, 通過 TCP 私有協議的形式對外提供服務,包括 DataServer, SessionServer 等,處理節點的注冊,續約和列表查詢等請求。

同時也基于 Http 協議提供控制接口,比如可以控制 session 節點是否開啟變更通知, 健康檢查接口等。

成員列表數據存儲在 Repository 中,Repository 被一致性協議層進行包裝,作為 SOFAJRaft 的狀態機實現,所有對 Repository 的操作都會同步到其他節點, 通過Rgistry來操作存儲層。

MetaServer 使用 Raft 協議保證高可用和數據一致性, 同時也會保持與注冊的節點的心跳,對于心跳超時沒有續約的節點進行驅逐,來保證數據的有效性。

在可用性方面,只要未超過半數節點掛掉,集群都可以正常對外提供服務,半數以上掛掉,Raft 協議無法選主和日志復制,因此無法保證注冊的成員數據的一致性和有效性。整個集群不可用 不會影響 Data 和 Session 節點的正常功能,只是無法感知節點列表變化。

2.2 問題

空談無用,just show the code。于是讓我們帶著問題來思考,即從宏觀和微觀角度來思考MetaServer應該實現什么功能,具體是怎么實現的。

思考:

  • MetaServer具體是以什么方式實現。
  • MetaServer如何實現高可用。
  • MetaServer如何實現或者應用內部通訊協議。
  • MetaServer如何保持DataNode列表和SessionNode列表。
  • MetaServer如何處理節點列表變更推送。
  • MetaServer如何處理節點列表查詢。
  • MetaServer如何處理MetaServer如何保證數據一致性。
  • 各個集群是如何搭建,如何完成高可用。

下面我們就一一分析。

0x03 代碼結構

我們在 sofa-registry-5.4.2/server/server/meta/src/main/java/com/alipay/sofa/registry/server/meta 看看目錄和文件結構。

按照目錄我們可以大致了解功能

  • MetaApplication.java :MetaServer程序主體,入口。
  • bootstrap :負責MetaServer的啟動和配置。
  • executor :負責各種定時管理任務,他的啟動設置是在 MetaServerBootstrap.initRaft ?之中。
  • listener ?:SOFARegistry 采用了 Handler - Task & Strategy - Listener 的方式來應對服務注冊中的各種場景和任務,這樣的處理模型能夠盡可能的讓代碼和架構清晰整潔。
  • node :對業務節點的抽象,包括DataNode,SessionNode,MetaNode。
  • registry :通過Registry來操作存儲層,所有對 Repository 的操作都會同步到其他節點。
  • remoting :對外交互接口,提供各種對外的 handler。
  • repository :集群節點列表存儲在 Repository 中,通過 Raft 強一致性協議對外提供節點注冊、續約、列表查詢等 Bolt 請求,從而保障集群獲得的數據是強一致性的。Repository 被一致性協議層進行包裝,作為 SOFAJRaft 的狀態機實現。
  • resource :http Server的接口,用來響應控制消息。
  • store :封裝了存儲節點的操作。
  • task :封裝了異步執行邏輯,通過TaskDispatcher,TaskExecutors 來執行各種定義好的異步Task。

具體代碼結構如下:

.
├──?MetaApplication.java
├──?bootstrap
│???├──?AbstractNodeConfigBean.java
│???├──?EnableMetaServer.java
│???├──?MetaServerBootstrap.java
│???├──?MetaServerConfig.java
│???├──?MetaServerConfigBean.java
│???├──?MetaServerConfiguration.java
│???├──?MetaServerInitializerConfiguration.java
│???├──?NodeConfig.java
│???├──?NodeConfigBeanProperty.java
│???└──?ServiceFactory.java
├──?executor
│???└──?ExecutorManager.java
├──?listener
│???├──?DataNodeChangePushTaskListener.java
│???├──?PersistenceDataChangeNotifyTaskListener.java
│???├──?ReceiveStatusConfirmNotifyTaskListener.java
│???└──?SessionNodeChangePushTaskListener.java
├──?node
│???├──?DataNodeService.java
│???├──?MetaNodeService.java
│???├──?NodeOperator.java
│???├──?NodeService.java
│???├──?SessionNodeService.java
│???└──?impl
│???????├──?DataNodeServiceImpl.java
│???????├──?MetaNodeServiceImpl.java
│???????└──?SessionNodeServiceImpl.java
├──?registry
│???├──?MetaServerRegistry.java
│???└──?Registry.java
├──?remoting
│???├──?DataNodeExchanger.java
│???├──?MetaClientExchanger.java
│???├──?MetaServerExchanger.java
│???├──?RaftExchanger.java
│???├──?SessionNodeExchanger.java
│???├──?connection
│???│???├──?DataConnectionHandler.java
│???│???├──?MetaConnectionHandler.java
│???│???├──?NodeConnectManager.java
│???│???└──?SessionConnectionHandler.java
│???└──?handler
│???????├──?AbstractServerHandler.java
│???????├──?DataNodeHandler.java
│???????├──?FetchProvideDataRequestHandler.java
│???????├──?GetNodesRequestHandler.java
│???????├──?RenewNodesRequestHandler.java
│???????└──?SessionNodeHandler.java
├──?repository
│???├──?NodeConfirmStatusService.java
│???├──?NodeRepository.java
│???├──?RepositoryService.java
│???├──?VersionRepositoryService.java
│???├──?annotation
│???│???└──?RaftAnnotationBeanPostProcessor.java
│???└──?service
│???????├──?DataConfirmStatusService.java
│???????├──?DataRepositoryService.java
│???????├──?MetaRepositoryService.java
│???????├──?SessionConfirmStatusService.java
│???????├──?SessionRepositoryService.java
│???????└──?SessionVersionRepositoryService.java
├──?resource
│???├──?BlacklistDataResource.java
│???├──?DecisionModeResource.java
│???├──?HealthResource.java
│???├──?MetaDigestResource.java
│???├──?MetaStoreResource.java
│???├──?PersistentDataResource.java
│???├──?RenewSwitchResource.java
│???└──?StopPushDataResource.java
├──?store
│???├──?DataStoreService.java
│???├──?MetaStoreService.java
│???├──?RenewDecorate.java
│???├──?SessionStoreService.java
│???└──?StoreService.java
└──?task
????├──?AbstractMetaServerTask.java
????├──?Constant.java
????├──?DataNodeChangePushTask.java
????├──?MetaServerTask.java
????├──?PersistenceDataChangeNotifyTask.java
????├──?ReceiveStatusConfirmNotifyTask.java
????├──?SessionNodeChangePushTask.java
????└──?processor
????????├──?DataNodeSingleTaskProcessor.java
????????├──?MetaNodeSingleTaskProcessor.java
????????└──?SessionNodeSingleTaskProcessor.java

16?directories,?75?files

0x04 啟動運行

啟動可以參考 ?https://www.sofastack.tech/projects/sofa-registry/server-quick-start/

SOFARegistry 支持兩種部署模式,分別是集成部署模式及獨立部署模式。

4.1 集成部署

4.1.1 Linux/Unix/Mac

啟動命令:sh bin/startup.sh

4.1.2 Windows

雙擊 bin 目錄下的 startup.bat 運行文件。

4.1.3 啟動信息

通過下列log我們可以看到啟動信息。

MetaApplication

[2020-09-12?20:23:05,463][INFO][main][MetaServerBootstrap]?-?Open?meta?server?port?9612?success!
[2020-09-12?20:23:08,198][INFO][main][MetaServerBootstrap]?-?Open?http?server?port?9615?success!
[2020-09-12?20:23:10,298][INFO][main][MetaServerBootstrap]?-?Raft?server?port?9614?start?success!group?RegistryGroup
[2020-09-12?20:23:10,322][INFO][main][MetaServerInitializerConfiguration]?-?Started?MetaServer

DataApplication

[2020-09-12?20:23:25,004][INFO][main][DataServerBootstrap]?-?Open?http?server?port?9622?success!
[2020-09-12?20:23:26,084][INFO][main][DataServerBootstrap]?-?start?server?success
[2020-09-12?20:23:26,094][INFO][main][DataApplication]?-?Started?DataApplication?in?10.217?seconds?(JVM?running?for?11.316)

SessionApplication

[2020-09-12?20:23:50,243][INFO][main][SessionServerBootstrap]?-?Open?http?server?port?9603?success!
[2020-09-12?20:23:50,464][INFO][main][SessionServerInitializer]?-?Started?SessionServer
[2020-09-12?20:23:50,526][INFO][main][SessionApplication]?-?Started?SessionApplication?in?12.516?seconds?(JVM?running?for?13.783)

各個 Server 的默認端口分別為:

meta.server.sessionServerPort=9610
meta.server.dataServerPort=9611
meta.server.metaServerPort=9612
meta.server.raftServerPort=9614
meta.server.httpServerPort=9615

可訪問三個角色提供的健康監測 API,或查看日志 logs/registry-startup.log:

#?查看meta角色的健康檢測接口:
$?curl?http://localhost:9615/health/check
{"success":true,"message":"...?raftStatus:Leader"}

#?查看data角色的健康檢測接口:
$?curl?http://localhost:9622/health/check
{"success":true,"message":"...?status:WORKING"}

#?查看session角色的健康檢測接口:
$?curl?http://localhost:9603/health/check
{"success":true,"message":"..."}

4.2 獨立部署

在這里我們可以看出來各種集群是如何搭建,以及如何在集群內部通訊,分布式協調。

按照常理來說,每個集群都應該依賴zookeeper之類的軟件來進行自己內部的協調,比如統一命名服務、狀態同步服務、集群管理、分布式應用配置項。但實際上我們沒有發現類似的使用。

具體看配置文件發現,每臺機器都要設置所有的metaServer的host。這說明Data Server, ?Session Server則強依賴Meta Server。

實際上,MetaServer 使用 Raft 協議保證高可用和數據一致性, 同時也會保持與注冊的節點的心跳,對于心跳超時沒有續約的節點進行驅逐,來保證數據的有效性。Meta 層能夠感知到 Session 節點和 Data 節點的變化,并通知集群的其它節點。

這就涉及到各個角色的 failover 機制:

  • MetaServer 集群部署,內部基于 Raft 協議選舉和復制,只要不超過 1?2 節點宕機,就可以對外服務。
  • DataServer 集群部署,基于一致性 Hash 承擔不同的數據分片,數據分片擁有多個副本,一個主副本和多個備副本。如果 DataServer 宕機,MetaServer 能感知,并通知所有 DataServer 和 SessionServer,數據分片可 failover 到其他副本,同時 DataServer 集群內部會進行分片數據的遷移。
  • SessionServer 集群部署,任何一臺 SessionServer 宕機時 Client 會自動 failover 到其他 SessionServer,并且 Client 會拿到最新的 SessionServer 列表,后續不會再連接這臺宕機的 SessionServer。

4.2.1 部署meta

每臺機器在部署時需要修改 conf/application.properties 配置:

#?將3臺meta機器的ip或hostname配置到下方(填入的hostname會被內部解析為ip地址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter

4.2.2 部署data

每臺機器在部署時需要修改 conf/application.properties 配置:

#?將3臺?meta?機器的?ip?或?hostname?配置到下方(填入的?hostname?會被內部解析為?ip?地址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter
data.server.numberOfReplicas=1000

4.2.3 部署 session

每臺機器在部署時需要修改 conf/application.properties 配置:

#?將3臺?meta?機器的?ip?或?hostname?配置到下方(填入的?hostname?會被內部解析為?ip?地址)
nodes.metaNode=DefaultDataCenter:,,
nodes.localDataCenter=DefaultDataCenter
nodes.localRegion=DefaultZone

0x05 總體邏輯

MetaServer 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

然后啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,集群數據查詢等 Http 接口。

最后啟動 Raft 服務, 每個節點同時作為 RaftClient 和 RaftServer, 用于集群間的變更和數據同步。

5.1 程序主體

MetaServer 是一個SpringBootApplication,主要起作用的就是EnableMetaServer。

@EnableMetaServer
@SpringBootApplication
public?class?MetaApplication?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(MetaApplication.class,?args);
????}
}

具體參見下圖

+-------------------+
|?@EnableMetaServer?|
|???????????????????|
|??MetaApplication??|
+-------------------+

EnableMetaServer注解引入了MetaServerConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(MetaServerConfiguration.class)
public?@interface?EnableMetaServer?{
}

MetaServerConfiguration是各種配置,并且引入 MetaServerInitializerConfiguration負責啟動。

@Configuration
@Import(MetaServerInitializerConfiguration.class)
@EnableConfigurationProperties
public?class?MetaServerConfiguration?{
}

所以我們可以知道:

  • MetaServerConfiguration :負責配置
  • MetaServerInitializerConfiguration :負責啟動

于是程序總結結構演化為下圖:

???????????????????????????????????(Init)
????????????????????????????+------------------------------------+
????????????????????????????|?MetaServerInitializerConfiguration?|
????????????????????????????+-------------+----------------------+
??????????????????????????????????????????^
+-------------------+?????????????????????|
|?@EnableMetaServer?|?????????????????????|
|???????????????????|?????????????????????|
|??MetaApplication??|?????????????????????|
+-------------+-----+?????????????????????|?(Configuration)
??????????????|?????????????????+---------+---------------+
??????????????+-------------->??|?MetaServerConfiguration?|
????????????????????????????????+-------------------------+

下面我們開始引入配置。

5.2 配置

MetaServer 模塊的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類為 MetaServerConfiguration。

5.2.1 配置分類

MetaServerConfiguration 具體有以下幾類配置:

  • Bootstrap,負責MetaServer啟動配置,是核心啟動類。
  • ServerConfig,負責MetaServer的配置項,比如MetaServerConfig,NodeConfig,PropertySplitter。
  • ServerServiceConfiguration,負責服務相關的配置,比如 sessionNodeService,storeServiceFactory,sessionStoreService。
  • ServerRepositoryConfiguration,負責Repository相關的配置,比如dataRepositoryService,sessionRepositoryService等。
  • ServerRemotingConfiguration,負責網絡相關配置,比如BoltExchange,JerseyExchange,這里隨后會重點說明。
  • ResourceConfiguration,負責Resource相關配置,比如 jerseyResourceConfig,persistentDataResource。
  • ServerTaskConfiguration,負責各種 ?task 相關配置 ,比如dataNodeSingleTaskProcessor。
  • ExecutorConfiguation,ExecutorManager相關配置。
  • MetaDBConfiguration,DB相關配置。

具體縮略版代碼如下 :

@Configuration
@Import(MetaServerInitializerConfiguration.class)
@EnableConfigurationProperties
public?class?MetaServerConfiguration?{
????@Bean
????@ConditionalOnMissingBean
????public?MetaServerBootstrap?metaServerBootstrap()?{
????}

????@Configuration
????protected?static?class?MetaServerConfigBeanConfiguration?{
????}

????@Configuration
????public?static?class?MetaServerServiceConfiguration?{
????}

????@Configuration
????public?static?class?MetaServerRepositoryConfiguration?{
????}

????@Configuration
????public?static?class?MetaServerRemotingConfiguration?{
????}

????@Configuration
????public?static?class?ResourceConfiguration?{
????}

????@Configuration
????public?static?class?MetaServerTaskConfiguration?{
????}

????@Configuration
????public?static?class?ExecutorConfiguation?{
????}

????@Configuration
????public?static?class?MetaDBConfiguration?{
????}??
}

我們的圖進化如下:

???????????????????????????????????(Init)
????????????????????????????+------------------------------------+
????????????????????????????|?MetaServerInitializerConfiguration?|
????????????????????????????+-------------+----------------------+??????+---------------------+
??????????????????????????????????????????^???????????????????????+-->??|?MetaServerBootstrap?|
+-------------------+?????????????????????|???????????????????????|?????+---------------------+
|?@EnableMetaServer?|?????????????????????|???????????????????????|?????+---------------------------------+
|???????????????????|?????????????????????|???????????????????????+-->??|MetaServerConfigBeanConfiguration|
|??MetaApplication??|?????????????????????|???????????????????????|?????+---------------------------------+
+--------------+----+?????????????????????|???????????????????????|?????+------------------------------+
???????????????|??????????????????????????|???????????????????????+-->??|MetaServerServiceConfiguration|
???????????????|??????????????????????????|???????????????????????|?????+------------------------------+
???????????????|??????????????????????????|???????????????????????|?????+---------------------------------+
???????????????|??????????????????????????|???????????????????????+-->??|MetaServerRepositoryConfiguration|
???????????????|??????????????????????????|???????????????????????|?????+---------------------------------+
???????????????|??????????????????????????|?(Configuration)???????|?????+-------------------------------+
???????????????|????????????????+---------+---------------+???????+-->??|MetaServerRemotingConfiguration|
???????????????+-------------->?|?MetaServerConfiguration?|?+-----+?????+-------------------------------+
????????????????????????????????+-------------------------+???????|?????+----------------------+
??????????????????????????????????????????????????????????????????+-->??|ResourceConfiguration?|
??????????????????????????????????????????????????????????????????|?????+----------------------+
??????????????????????????????????????????????????????????????????|?????+---------------------------+
??????????????????????????????????????????????????????????????????+-->??|MetaServerTaskConfiguration|
??????????????????????????????????????????????????????????????????|?????+---------------------------+
??????????????????????????????????????????????????????????????????|?????+---------------------+
??????????????????????????????????????????????????????????????????+-->??|ExecutorConfiguation?|
??????????????????????????????????????????????????????????????????|?????+---------------------+
??????????????????????????????????????????????????????????????????|?????+--------------------+
??????????????????????????????????????????????????????????????????+-->??|MetaDBConfiguration?|
????????????????????????????????????????????????????????????????????????+--------------------+

下圖為了手機閱讀

7cc1ad3dc7acfaaacf905c236d6af29a.png

5.2.2 handler的配置

這里要特殊提一下handler的配置,因為這是后續分析的主體之一,是三個 Bolt Server的handler。

@Configuration
public?static?class?MetaServerRemotingConfiguration?{

????@Bean
????public?Exchange?boltExchange()?{
????????return?new?BoltExchange();
????}

????@Bean
????public?Exchange?jerseyExchange()?{
????????return?new?JerseyExchange();
????}

????@Bean(name?=?"sessionServerHandlers")
????public?Collection?sessionServerHandlers()?{
????????Collection?list?=?new?ArrayList<>();
????????list.add(sessionConnectionHandler());
????????list.add(sessionNodeHandler());
????????list.add(renewNodesRequestHandler());
????????list.add(getNodesRequestHandler());
????????list.add(fetchProvideDataRequestHandler());return?list;
????}@Bean(name?=?"dataServerHandlers")public?Collection?dataServerHandlers()?{
????????Collection?list?=?new?ArrayList<>();
????????list.add(dataConnectionHandler());
????????list.add(getNodesRequestHandler());
????????list.add(dataNodeHandler());
????????list.add(renewNodesRequestHandler());
????????list.add(fetchProvideDataRequestHandler());return?list;
????}@Bean(name?=?"metaServerHandlers")public?Collection?metaServerHandlers()?{
????????Collection?list?=?new?ArrayList<>();
????????list.add(metaConnectionHandler());
????????list.add(getNodesRequestHandler());return?list;
????}
}

于是我們的總體架構進化具體見下圖

???????????????????????????????(Init)
????????????????????????+------------------------------------+
????????????????????????|?MetaServerInitializerConfiguration?|
????????????????????????+--------------+---------------------+
???????????????????????????????????????^
???????????????????????????????????????|
???????????????????????????????????????|
???????????????????????????????????????|
???????????????????????????????????????|
???????????????????????????????????????|?????????????????????????????+---------------------+
???????????????????????????????????????|???????????????????????+-->??|?MetaServerBootstrap?|
+-------------------+??????????????????|???????????????????????|?????+---------------------+
|?@EnableMetaServer?|??????????????????|???????????????????????|?????+---------------------------------+
|???????????????????|??????????????????|???????????????????????+-->??|MetaServerConfigBeanConfiguration|
|??MetaApplication??|??????????????????|???????????????????????|?????+---------------------------------+
+--------------+----+??????????????????|???????????????????????|?????+------------------------------+?????????????+-----------------------+
???????????????|???????????????????????|???????????????????????+-->??|MetaServerServiceConfiguration|??????+--->??|?sessionServerHandlers?|
???????????????|???????????????????????|???????????????????????|?????+------------------------------+??????|??????+-----------------------+
???????????????|???????????????????????|???????????????????????|?????+---------------------------------+???|??????+--------------------+
???????????????|???????????????????????|???????????????????????+-->??|MetaServerRepositoryConfiguration+------->??|?dataServerHandlers?|
???????????????|???????????????????????|???????????????????????|?????+---------------------------------+???|??????+--------------------+
???????????????|???????????????????????|?(Configuration)???????|?????+-------------------------------+?????|??????+--------------------+
???????????????|?????????????+---------+---------------+???????+-->??|MetaServerRemotingConfiguration|?????+--->??|?metaServerHandlers?|
???????????????+----------->?|?MetaServerConfiguration?|?+-----+?????+-------------------------------+????????????+--------------------+
?????????????????????????????+-------------------------+???????|?????+----------------------+
???????????????????????????????????????????????????????????????+-->??|ResourceConfiguration?|
???????????????????????????????????????????????????????????????|?????+----------------------+
???????????????????????????????????????????????????????????????|?????+---------------------------+
???????????????????????????????????????????????????????????????+-->??|MetaServerTaskConfiguration|
???????????????????????????????????????????????????????????????|?????+---------------------------+
???????????????????????????????????????????????????????????????|?????+---------------------+
???????????????????????????????????????????????????????????????+-->??|ExecutorConfiguation?|
???????????????????????????????????????????????????????????????|?????+---------------------+
???????????????????????????????????????????????????????????????|?????+--------------------+
???????????????????????????????????????????????????????????????+-->??|MetaDBConfiguration?|
?????????????????????????????????????????????????????????????????????+--------------------+

手機上閱讀如下:

71701f576924b0a02c14094e2a39640c.png

關于handler配置,進一步細化如下圖

???????????????????????????????(Init)

????????????????????????????????????????????????????????????????????????????????????????????????????????????????+-->??sessionConnectionHandler
????????????????????????????????????????????????????????????????????????????????????????????????????????????????|
????????????????????????????????????????????????????????????????????????????????????????????????????????????????|
????????????????????????????????????????????????????????????????????????????????????????????????????????????????+-->??sessionNodeHandler
?????????????????????????????????????????????????????????????????????????????????????+-----------------------+??|
??????????????????????????????????????????????????????????????????????????????+--->??|?sessionServerHandlers?+--+
????????????????????????????????????????+---------------------+???????????????|??????+-----------------------+??+-->??renewNodesRequestHandler
??????????????????????????????????+-->??|?MetaServerBootstrap?|???????????????|?????????????????????????????????|
??????????????????????????????????|?????+---------------------+???????????????|?????????????????????????????????|
??????????????????????????????????|?????+---------------------------------+???|?????????????????????????????????+-->??getNodesRequestHandler
??????????????????????????????????+-->??|MetaServerConfigBeanConfiguration|???|?????????????????????????????????|
??????????????????????????????????|?????+---------------------------------+???|?????????????????????????????????|
??????????????????????????????????|?????+------------------------------+??????|?????????????????????????????????+-->??fetchProvideDataRequestHandler
??????????????????????????????????+-->??|MetaServerServiceConfiguration|??????|
??????????????????????????????????|?????+------------------------------+??????|
??????????????????????????????????|?????+---------------------------------+???|?????????????????????????????????+-->??dataConnectionHandler
??????????????????????????????????+-->??|MetaServerRepositoryConfiguration+---+?????????????????????????????????|
??????????????????????????????????|?????+---------------------------------+???|?????????????????????????????????|
??????????????????????????????????|?????+-------------------------------+?????|??????+--------------------+?????+-->??getNodesRequestHandler
+-------------------------+???????+-->??|MetaServerRemotingConfiguration|?????+--->??|?dataServerHandlers?+-----+
|?MetaServerConfiguration?|?+-----+?????+-------------------------------+?????|??????+--------------------+?????|
+-------------------------+???????|?????+----------------------+??????????????|?????????????????????????????????+-->??dataNodeHandler
??????????????????????????????????+-->??|ResourceConfiguration?|??????????????|?????????????????????????????????|
??????????????????????????????????|?????+----------------------+??????????????|?????????????????????????????????|
??????????????????????????????????|?????+---------------------------+?????????|?????????????????????????????????+-->??renewNodesRequestHandler
??????????????????????????????????+-->??|MetaServerTaskConfiguration|?????????|?????????????????????????????????|
??????????????????????????????????|?????+---------------------------+?????????|?????????????????????????????????|
??????????????????????????????????|?????+---------------------+???????????????|?????????????????????????????????+-->??fetchProvideDataRequestHandler
??????????????????????????????????+-->??|ExecutorConfiguation?|???????????????|
??????????????????????????????????|?????+---------------------+???????????????|
??????????????????????????????????|?????+--------------------+????????????????|
??????????????????????????????????+-->??|MetaDBConfiguration?|????????????????|?????????????????????????????????+--->?metaConnectionHandler
????????????????????????????????????????+--------------------+????????????????|??????+--------------------+?????|
??????????????????????????????????????????????????????????????????????????????+---->?|?metaServerHandlers?+-----+
?????????????????????????????????????????????????????????????????????????????????????+--------------------+?????+--->?getNodesRequestHandler

手機上如圖

9eb656d66c13edbc260d33167900ea04.png

這個就對應了參考中的圖例:

MetaServer 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

bec365e2b371729bb79fd86eddc88243.png
meta-server
  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

0x06 啟動

系統是通過對MetaServerBootstrap的控制來完成了啟動。

MetaServer 模塊的各個 bean 在 JavaConfig 中統一配置,JavaConfig 類為 MetaServerConfiguration。

啟動入口類為 MetaServerInitializerConfiguration,該類不由 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啟動時由 Spring 框架調用其 start 方法。

public?class?MetaServerInitializerConfiguration?implements?SmartLifecycle?{
????@Autowired
????private?MetaServerBootstrap?metaServerBootstrap;

????@Override
????public?void?start()?{
?????metaServerBootstrap.start();
???MetaServerInitializerConfiguration.this.running.set(true);
????}

????@Override
????public?void?stop()?{
????????this.running.set(false);
????????metaServerBootstrap.destroy();
????}
}

具體見下圖,因為 metaServerBootstrap 是通過配置生成,所以init過程指向配置部分。

???????????????????????????????(Init)
????????????????????????+------------------------------------+??start,stop
????????????????????????|?MetaServerInitializerConfiguration?+----------------+
????????????????????????+--------------+---------------------+????????????????|
???????????????????????????????????????^??????????????????????????????????????|
???????????????????????????????????????|??????????????????????????????????????|
???????????????????????????????????????|??????????????????????????????????????|
???????????????????????????????????????|??????????????????????????????????????|
???????????????????????????????????????|??????????????????????????????????????v
???????????????????????????????????????|?????????????????????????????+---------------------+
???????????????????????????????????????|???????????????????????+-->??|?MetaServerBootstrap?|
+-------------------+??????????????????|???????????????????????|?????+---------------------+
|?@EnableMetaServer?|??????????????????|???????????????????????|?????+---------------------------------+
|???????????????????|??????????????????|???????????????????????+-->??|MetaServerConfigBeanConfiguration|
|??MetaApplication??|??????????????????|???????????????????????|?????+---------------------------------+
+--------------+----+??????????????????|???????????????????????|?????+------------------------------+?????????????+-----------------------+
???????????????|???????????????????????|???????????????????????+-->??|MetaServerServiceConfiguration|??????+--->??|?sessionServerHandlers?|
???????????????|???????????????????????|???????????????????????|?????+------------------------------+??????|??????+-----------------------+
???????????????|???????????????????????|???????????????????????|?????+---------------------------------+???|??????+--------------------+
???????????????|???????????????????????|???????????????????????+-->??|MetaServerRepositoryConfiguration+------->??|?dataServerHandlers?|
???????????????|???????????????????????|???????????????????????|?????+---------------------------------+???|??????+--------------------+
???????????????|???????????????????????|?(Configuration)???????|?????+-------------------------------+?????|??????+--------------------+
???????????????|?????????????+---------+---------------+???????+-->??|MetaServerRemotingConfiguration|?????+--->??|?metaServerHandlers?|
???????????????+----------->?|?MetaServerConfiguration?|?+-----+?????+-------------------------------+????????????+--------------------+
?????????????????????????????+-------------------------+???????|?????+----------------------+
???????????????????????????????????????????????????????????????+-->??|ResourceConfiguration?|
???????????????????????????????????????????????????????????????|?????+----------------------+
???????????????????????????????????????????????????????????????|?????+---------------------------+
???????????????????????????????????????????????????????????????+-->??|MetaServerTaskConfiguration|
???????????????????????????????????????????????????????????????|?????+---------------------------+
???????????????????????????????????????????????????????????????|?????+---------------------+
???????????????????????????????????????????????????????????????+-->??|ExecutorConfiguation?|
???????????????????????????????????????????????????????????????|?????+---------------------+
???????????????????????????????????????????????????????????????|?????+--------------------+
???????????????????????????????????????????????????????????????+-->??|MetaDBConfiguration?|
?????????????????????????????????????????????????????????????????????+--------------------+

手機上如下

90cc69d13c772dfe2ed3b28233b3bf89.png

6.1 架構

MetaServerBootstrap是核心啟動類,該類主要包含了三類組件:外部節點通信組件、Raft 服務通信組件、定時器組件。

  • 外部節點通信組件:在該類中有幾個 Server 通信對象,用于和其它外部節點進行通信。其中 httpServer 主要提供一系列 http 接口,用于 dashboard 管理、數據查詢等;sessionServer 主要是處理一些session相關的服務;dataServer 則負責數據相關服務;metaServer 負責meta server的注冊;

  • Raft 服務 :用于集群間的變更和數據同步,raftExchanger 就起到這個作用;

  • 定時器組件:例如定時檢測節點信息、定時檢測數據版本信息;具體可見 ExecutorManager,這是一個啟動各種管理線程的地方。他的啟動設置是在 MetaServerBootstrap.initRaft ?之中 。

6.2 類定義

MetaServerBootstrap的定義如下:

public?class?MetaServerBootstrap?{
????@Autowired
????private?MetaServerConfig??????????????????metaServerConfig;

????@Autowired
????private?Exchange??????????????????????????boltExchange;

????@Autowired
????private?Exchange??????????????????????????jerseyExchange;

????@Autowired
????private?ExecutorManager???????????????????executorManager;

????@Resource(name?=?"sessionServerHandlers")
????private?Collection?sessionServerHandlers;@Resource(name?=?"dataServerHandlers")private?Collection?dataServerHandlers;@Resource(name?=?"metaServerHandlers")private?Collection?metaServerHandlers;@Autowiredprivate?ResourceConfig????????????????????jerseyResourceConfig;@Autowiredprivate?ApplicationContext????????????????applicationContext;@Autowiredprivate?RaftExchanger?????????????????????raftExchanger;private?Server????????????????????????????sessionServer;private?Server????????????????????????????dataServer;private?Server????????????????????????????metaServer;private?Server????????????????????????????httpServer;
}

可以參見下圖

?????????????????????????????????????????????????????????????????????????????????????????+-----------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|?metaServerConfig|
?????????????????????????????????????????????????????????????????????????????????|???????+-----------------+
?????????????????????????????????????????????????????????????????????????????????|???????+--------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|?boltExchange?|
?????????????????????????????????????????????????????????????????????????????????|???????+--------------+
?????????????????????????????????????????????????????????????????????????????????|???????+--------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|jerseyExchange|
?????????????????????????????????????????????????????????????????????????????????|???????+--------------+
?????????????????????????????????????????????????????????????????????????????????|???????+---------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|executorManager|
?????????????????????????????????????????????????????????????????????????????????|???????+---------------+
?????????????????????????????????????????????????????????????????????????????????|???????+---------------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|sessionServerHandlers|
?????????????????????????????????????????????????????????????????????????????????|???????+---------------------+
?????????????????????????????????????????????????????????????????????????????????|???????+-----------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|dataServerHandler|
???????(Init)????????????????????????????????????????????????????????????????????|???????+-----------------+
+------------------------------------+??start,stop???+---------------------+?????|???????+------------------+
|?MetaServerInitializerConfiguration?+------------->?|?MetaServerBootstrap?|?+-------->??|metaServerHandlers|
+------------------------------------+???????????????+---------------------+?????|???????+------------------+
?????????????????????????????????????????????????????????????????????????????????|???????+-------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|raftExchanger|
?????????????????????????????????????????????????????????????????????????????????|???????+-------------+
?????????????????????????????????????????????????????????????????????????????????|???????+-------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|sessionServer|
?????????????????????????????????????????????????????????????????????????????????|???????+-------------+
?????????????????????????????????????????????????????????????????????????????????|???????+-----------+
?????????????????????????????????????????????????????????????????????????????????+---->??|dataServer?|
?????????????????????????????????????????????????????????????????????????????????|???????+-----------+
?????????????????????????????????????????????????????????????????????????????????|???????+-----------+
?????????????????????????????????????????????????????????????????????????????????+---->??|metaServer?|
?????????????????????????????????????????????????????????????????????????????????|???????+-----------+
?????????????????????????????????????????????????????????????????????????????????|???????+----------+
?????????????????????????????????????????????????????????????????????????????????+---->??|httpServer|
?????????????????????????????????????????????????????????????????????????????????|???????+----------+
?????????????????????????????????????????????????????????????????????????????????|???????+---------------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|jerseyResourceConfig?|
?????????????????????????????????????????????????????????????????????????????????|???????+---------------------+
?????????????????????????????????????????????????????????????????????????????????|???????+-------------------+
?????????????????????????????????????????????????????????????????????????????????+---->??|applicationContext?|
?????????????????????????????????????????????????????????????????????????????????????????+-------------------+

手機參見下圖

2d6a7c65444879553d89c2b61ee46a6f.png

6.3 通信 Exchange

因為前面代碼中有

@Autowired
private?Exchange??????????????????????????boltExchange;

@Autowired
private?Exchange??????????????????????????jerseyExchange;

這里要特殊說明下Exchange。

Exchange 作為 Client / Server 連接的抽象,負責節點之間的連接。在建立連接中,可以設置一系列應對不同任務的 handler (稱之為 ChannelHandler),這些 ChannelHandler 有的作為 Listener 用來處理連接事件,有的作為 Processor 用來處理各種指定的事件,比如服務信息數據變化、Subscriber 注冊等事件。

afc92a6ddfd4d9cbc51494dba0070df8.png
圖4 - 每一層各司其職,協同實現節點通信

圖 - 每一層各司其職,協同實現節點通信

各種節點在啟動的時候,利用 Exchange 設置了一系列 ChannelHandler,比如:

private?void?openDataRegisterServer()?{
????try?{
????????if?(dataStart.compareAndSet(false,?true))?{
????????????dataServer?=?boltExchange.open(new?URL(NetUtil.getLocalAddress().getHostAddress(),
????????????????metaServerConfig.getDataServerPort()),?dataServerHandlers
????????????????.toArray(new?ChannelHandler[dataServerHandlers.size()]));
????????}
????}?
}

6.4 啟動入口

前面已經提到,啟動入口類為 MetaServerInitializerConfiguration,該類不由 JavaConfig 管理配置,而是繼承了 SmartLifecycle 接口,在啟動時由 Spring 框架調用其 start 方法。

該方法中調用了 MetaServerBootstrap # start 方法,用于啟動一系列的初始化服務。

import?org.springframework.beans.factory.annotation.Autowired;
import?org.springframework.context.SmartLifecycle;

public?class?MetaServerInitializerConfiguration?implements?SmartLifecycle?{
????@Autowired
????private?MetaServerBootstrap?metaServerBootstrap;‘
??????
????@Override
????public?void?start()?{
????????????metaServerBootstrap.start();
????????????MetaServerInitializerConfiguration.this.running.set(true);
????}
}??

6.4.1 啟動服務Server

前面提到,MetaServerBootstrap 在啟動時,會啟動三個 Bolt Server,并且注冊 Processor Handler,處理對應的請求:

  • DataServer:處理 DataNode 相關的請求;
  • SessionServer:處理 SessionNode 相關的請求;
  • MetaServer:處理MetaNode相關的請求;

然后啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,集群數據查詢等 Http 接口。

最后啟動 Raft 服務, 每個節點同時作為 RaftClient 和 RaftServer, 用于集群間的變更和數據同步。為支持高可用特性,對于 MetaServer 來說,存儲了 SOFARegistry 的元數據,為了保障 MetaServer 集群的一致性,其采用了 Raft 協議來進行選舉和復制。

具體代碼參見:

public?void?start()?{
????????openSessionRegisterServer();
????????openDataRegisterServer();
????????openMetaRegisterServer();
????????openHttpServer();
????????initRaft();
}

private?void?openHttpServer()?{
????????if?(httpStart.compareAndSet(false,?true))?{
????????????bindResourceConfig();
????????????httpServer?=?jerseyExchange.open(
????????????????new?URL(NetUtil.getLocalAddress().getHostAddress(),?metaServerConfig
????????????????????.getHttpServerPort()),?new?ResourceConfig[]?{?jerseyResourceConfig?});
????????}
}

private?void?initRaft()?{
????raftExchanger.startRaftServer(executorManager);
????raftExchanger.startRaftClient();
????raftExchanger.startCliService();
}

6.4.1.1 BoltServer

Raft 和 Bolt 是SOFA所特殊實現,所以我們暫不介紹其底層機制,以后有機會單獨開篇,下面提一下三個BoltServer。

private?void?openSessionRegisterServer()?{
????????if?(sessionStart.compareAndSet(false,?true))?{
????????????sessionServer?=?boltExchange
????????????????.open(
????????????????????new?URL(NetUtil.getLocalAddress().getHostAddress(),?metaServerConfig
????????????????????????.getSessionServerPort()),?sessionServerHandlers
????????????????????????.toArray(new?ChannelHandler[sessionServerHandlers.size()]));

????????}
}

private?void?openDataRegisterServer()?{
????????if?(dataStart.compareAndSet(false,?true))?{
????????????dataServer?=?boltExchange.open(new?URL(NetUtil.getLocalAddress().getHostAddress(),
????????????????metaServerConfig.getDataServerPort()),?dataServerHandlers
????????????????.toArray(new?ChannelHandler[dataServerHandlers.size()]));
????????}
}

private?void?openMetaRegisterServer()?{
????????if?(metaStart.compareAndSet(false,?true))?{
????????????metaServer?=?boltExchange.open(new?URL(NetUtil.getLocalAddress().getHostAddress(),
????????????????metaServerConfig.getMetaServerPort()),?metaServerHandlers
????????????????.toArray(new?ChannelHandler[metaServerHandlers.size()]));
????????}
}

這幾個Server的handler就是我們前面配置的

@Resource(name?=?"sessionServerHandlers")
private?Collection?sessionServerHandlers;@Resource(name?=?"dataServerHandlers")private?Collection?dataServerHandlers;@Resource(name?=?"metaServerHandlers")private?Collection?metaServerHandlers;

具體參見下圖:

????????????????????????????????????????????????????????????????????????????????????????????????????????+------------------------+
????????????????????????????????????+-----------------+???????????????????????????????????????????+--->?|sessionConnectionHandler|
????????????????????????????+---->??|?metaServerConfig|???????????????????????????????????????????|?????+------------------------+
????????????????????????????|???????+-----------------+???????????????????????????????????????????|?????+-------------------+
????????????????????????????|???????+--------------+??????????????????????????????????????????????+--->?|sessionNodeHandler?|
????????????????????????????+---->??|?boltExchange?|??????????????????????????????????????????+-->+?????+-------------------+
????????????????????????????|???????+--------------+??????????????????????????????????????????|???|?????+------------------------+
????????????????????????????|???????+--------------+??????????????????????????????????????????|???+--->?|renewNodesRequestHandler|
????????????????????????????+---->??|jerseyExchange|??????????????????????????????????????????|???|?????+------------------------+
????????????????????????????|???????+--------------+??????????????????????????????????????????|???|?????+----------------------+
????????????????????????????|???????+---------------+?????????????????????????????????????????|???+--->?|getNodesRequestHandler|
????????????????????????????+---->??|executorManager|?????????????????????????????????????????|???|?????+----------------------+
????????????????????????????|???????+---------------+???????????????+---------------------+???|???|?????+------------------------------+
????????????????????????????+------------------------------------->?|sessionServerHandlers+---+???+--->?|fetchProvideDataRequestHandler|
????????????????????????????|???????+-------------+?????????????????+---------------------+?????????????+------------------------------+
????????????????????????????+---->??|sessionServer|?+------------------^
????????????????????????????|???????+-------------+?????????????????????????????????????????????????????+---------------------+
????????????????????????????|?????????????????????????????????????????????????????????????????????---->?|dataConnectionHandler|
????????????????????????????|???????????????????????????????????????+------------------+??????????|?????+---------------------+
+---------------------+?????+------------------------------------->?|dataServerHandlers+----------+?????+----------------------+
|?MetaServerBootstrap?|?+---+???????+-----------+???????????????????+------------------+??????????+--->?|getNodesRequestHandler|
+---------------------+?????+---->??|dataServer?+----------------------^??????????????????????????|?????+----------------------+
????????????????????????????|???????+-----------+?????????????????????????????????????????????????|?????+---------------+
????????????????????????????|?????????????????????????????????????????????????????????????????????+--->?|dataNodeHandler|
????????????????????????????|???????????????????????????????????????+------------------+??????????|?????+---------------+
????????????????????????????+------------------------------------>??|metaServerHandlers+------+???|?????+------------------------+
????????????????????????????|???????+-----------+???????????????????+------------------+??????|???+--->?|renewNodesRequestHandler|
????????????????????????????+---->??|metaServer?+----------------------^??????????????????????|???|?????+------------------------+
????????????????????????????|???????+-----------+?????????????????????????????????????????????|???|?????+------------------------------+
????????????????????????????|?????????????????????????????????????????????????????????????????|???+--->?|fetchProvideDataRequestHandler|
????????????????????????????|???????+-------------+???????????????????????????????????????????|?????????+------------------------------+
????????????????????????????+---->??|raftExchanger|???????????????????????????????????????????|
????????????????????????????|???????+-------------+???????????????????????????????????????????|
????????????????????????????|?????????????????????????????????????????????????????????????????|
????????????????????????????|???????+----------+??????????????????????????????????????????????|
????????????????????????????+---->??|httpServer|??????????????????????????????????????????????|??????????+---------------------+
????????????????????????????|???????+----------+??????????????????????????????????????????????|????+-->??|metaConnectionHandler|
????????????????????????????|???????+---------------------+???????????????????????????????????+---->?????+---------------------+
????????????????????????????+---->??|jerseyResourceConfig?|????????????????????????????????????????|?????+---------------------+
????????????????????????????|???????+---------------------+????????????????????????????????????????+-->??|getNodesRequestHandle|
????????????????????????????|???????+-------------------+????????????????????????????????????????????????+---------------------+
????????????????????????????+---->??|applicationContext?|
????????????????????????????????????+-------------------+

手機上

e64add5500c7120e59830eccc680b74e.png

在初始化時候,大致堆棧如下 ?:

interest:55,?RenewNodesRequestHandler?(com.alipay.sofa.registry.server.meta.remoting.handler)
interest:61,?SyncUserProcessorAdapter?(com.alipay.sofa.registry.remoting.bolt)
registerUserProcessor:42,?UserProcessorRegisterHelper?(com.alipay.remoting.rpc.protocol)
registerUserProcessor:376,?RpcServer?(com.alipay.remoting.rpc)
registerUserProcessorHandler:159,?BoltServer?(com.alipay.sofa.registry.remoting.bolt)
initHandler:139,?BoltServer?(com.alipay.sofa.registry.remoting.bolt)
startServer:92,?BoltServer?(com.alipay.sofa.registry.remoting.bolt)
open:65,?BoltExchange?(com.alipay.sofa.registry.remoting.bolt.exchange)
open:36,?BoltExchange?(com.alipay.sofa.registry.remoting.bolt.exchange)
openSessionRegisterServer:149,?MetaServerBootstrap?(com.alipay.sofa.registry.server.meta.bootstrap)
start:108,?MetaServerBootstrap?(com.alipay.sofa.registry.server.meta.bootstrap)
start:51,?MetaServerInitializerConfiguration?(com.alipay.sofa.registry.server.meta.bootstrap)

以SessionServer為例,在構建過程中,調用到 ?BoltExchange . open。

private?void?openSessionRegisterServer()?{
????????if?(sessionStart.compareAndSet(false,?true))?{
????????????sessionServer?=?boltExchange
????????????????.open(
????????????????????new?URL(NetUtil.getLocalAddress().getHostAddress(),?metaServerConfig
????????????????????????.getSessionServerPort()),?sessionServerHandlers
????????????????????????.toArray(new?ChannelHandler[sessionServerHandlers.size()]));
????????}???
}

BoltExchange中有

@Override
public?Server?open(URL?url,?ChannelHandler...?channelHandlers)?{
????BoltServer?server?=?createBoltServer(url,?channelHandlers);
????setServer(server,?url);
????server.startServer();
????return?server;
}

BoltServer中有

public?void?startServer()?{
????if?(isStarted.compareAndSet(false,?true))?{
????????????boltServer?=?new?RpcServer(url.getPort(),?true);
????????????initHandler();
????????????boltServer.start();
????}?
}

private?void?initHandler()?{
????????if?(initHandler.compareAndSet(false,?true))?{
????????????boltServer.addConnectionEventProcessor(ConnectionEventType.CONNECT,
????????????????new?ConnectionEventAdapter(ConnectionEventType.CONNECT,
????????????????????getConnectionEventHandler(),?this));
????????????boltServer.addConnectionEventProcessor(ConnectionEventType.CLOSE,
????????????????new?ConnectionEventAdapter(ConnectionEventType.CLOSE,?getConnectionEventHandler(),
????????????????????this));
????????????boltServer.addConnectionEventProcessor(ConnectionEventType.EXCEPTION,
????????????????new?ConnectionEventAdapter(ConnectionEventType.EXCEPTION,
????????????????????getConnectionEventHandler(),?this));

????????????registerUserProcessorHandler();
????????}
}

最后調用,會設定同步和異步的handler。

private?void?registerUserProcessorHandler()?{
????if?(channelHandlers?!=?null)?{
????????for?(ChannelHandler?channelHandler?:?channelHandlers)?{
????????????if?(HandlerType.PROCESSER.equals(channelHandler.getType()))?{
????????????????if?(InvokeType.SYNC.equals(channelHandler.getInvokeType()))?{
????????????????????boltServer.registerUserProcessor(new?SyncUserProcessorAdapter(
????????????????????????channelHandler));
????????????????}?else?{
????????????????????boltServer.registerUserProcessor(new?AsyncUserProcessorAdapter(
????????????????????????channelHandler));
????????????????}
????????????}
????????}
????}
}

6.4.1.2 HttpServer

以使用 Jetty 的 openHttpServer 為例

啟動 HttpServer, 用于處理 Admin 請求,提供推送開關,集群數據查詢等 Http 接口。

public?class?JerseyJettyServer?implements?Server?{
????public?static?org.eclipse.jetty.server.Server?createServer(final?URI?uri,final?ResourceConfig?resourceConfig,final?boolean?start)?{

????????JettyHttpContainer?handler?=?ContainerFactory.createContainer(JettyHttpContainer.class,
????????????resourceConfig);

????????int?defaultPort?=?Container.DEFAULT_HTTP_PORT;
????????final?int?port?=?(uri.getPort()?==?-1)???defaultPort?:?uri.getPort();

????????final?org.eclipse.jetty.server.Server?server?=?new?org.eclipse.jetty.server.Server(
????????????new?JettyConnectorThreadPool());

????????final?ServerConnector?http?=?new?ServerConnector(server,?new?HttpConnectionCustomFactory());
????????http.setPort(port);
????????server.setConnectors(new?Connector[]?{?http?});

????????if?(handler?!=?null)?{
????????????server.setHandler(handler);
????????}

????????if?(start)?{
????????????try?{
????????????????//?Start?the?server.
????????????????server.start();
????????????}?
????????}
????????return?server;
????}??
}

其堆棧如下:

:72,?JerseyJettyServer?(com.alipay.sofa.registry.remoting.jersey)
open:73,?JerseyExchange?(com.alipay.sofa.registry.remoting.jersey.exchange)
open:40,?JerseyExchange?(com.alipay.sofa.registry.remoting.jersey.exchange)
openHttpServer:205,?MetaServerBootstrap?(com.alipay.sofa.registry.server.meta.bootstrap)
start:114,?MetaServerBootstrap?(com.alipay.sofa.registry.server.meta.bootstrap)
start:51,?MetaServerInitializerConfiguration?(com.alipay.sofa.registry.server.meta.bootstrap)
doStart:173,?DefaultLifecycleProcessor?(org.springframework.context.support)
access$200:50,?DefaultLifecycleProcessor?(org.springframework.context.support)
start:350,?DefaultLifecycleProcessor$LifecycleGroup?(org.springframework.context.support)
startBeans:149,?DefaultLifecycleProcessor?(org.springframework.context.support)
onRefresh:112,?DefaultLifecycleProcessor?(org.springframework.context.support)
finishRefresh:880,?AbstractApplicationContext?(org.springframework.context.support)
refresh:546,?AbstractApplicationContext?(org.springframework.context.support)
refresh:693,?SpringApplication?(org.springframework.boot)
refreshContext:360,?SpringApplication?(org.springframework.boot)
run:303,?SpringApplication?(org.springframework.boot)
run:1118,?SpringApplication?(org.springframework.boot)
run:1107,?SpringApplication?(org.springframework.boot)
main:42,?MetaApplication?(com.alipay.sofa.registry.server.meta)

6.4.1.3 @RaftService

如下存儲由Raft來保證數據一致性,后文針對此有詳細講解。

@RaftService(uniqueId?=?"sessionServer")
public?class?SessionVersionRepositoryService?
@RaftService(uniqueId?=?"metaServer")
public?class?MetaRepositoryService??
@RaftService(uniqueId?=?"dataServer")
public?class?DataRepositoryService??
@RaftService(uniqueId?=?"sessionServer")
public?class?SessionRepositoryService???
@RaftService(uniqueId?=?"dataServer")
public?class?DataConfirmStatusService???
@RaftService(uniqueId?=?"sessionServer")
public?class?SessionConfirmStatusService??

6.4.2 ExecutorManager

是一個啟動各種管理線程的地方,都是定期常規管理任務。

public?class?ExecutorManager?{

????private?ScheduledExecutorService?scheduler;

????private?ThreadPoolExecutor???????heartbeatCheckExecutor;
????private?ThreadPoolExecutor???????checkDataChangeExecutor;
????private?ThreadPoolExecutor???????getOtherDataCenterChangeExecutor;
????private?ThreadPoolExecutor???????connectMetaServerExecutor;
????private?ThreadPoolExecutor???????checkNodeListChangePushExecutor;
????private?ThreadPoolExecutor???????raftClientRefreshExecutor;

????private?MetaServerConfig?????????metaServerConfig;

????@Autowired
????private?Registry?????????????????metaServerRegistry;

????@Autowired
????private?MetaClientExchanger??????metaClientExchanger;

????@Autowired
????private?RaftExchanger????????????raftExchanger;
??
???public?void?startScheduler()?{

????????init();

????????scheduler.schedule(new?TimedSupervisorTask("HeartbeatCheck",?scheduler,?heartbeatCheckExecutor,
????????????????????????metaServerConfig.getSchedulerHeartbeatTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerHeartbeatExpBackOffBound(),?()?->?metaServerRegistry.evict()),
????????????????metaServerConfig.getSchedulerHeartbeatFirstDelay(),?TimeUnit.SECONDS);

????????scheduler.schedule(
????????????????new?TimedSupervisorTask("GetOtherDataCenterChange",?scheduler,?getOtherDataCenterChangeExecutor,
????????????????????????metaServerConfig.getSchedulerGetDataChangeTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerGetDataChangeExpBackOffBound(),?()?->?{
????????????????????metaServerRegistry.getOtherDataCenterNodeAndUpdate(NodeType.DATA);
????????????????????metaServerRegistry.getOtherDataCenterNodeAndUpdate(NodeType.META);
????????????????}),?metaServerConfig.getSchedulerGetDataChangeFirstDelay(),?TimeUnit.SECONDS);

????????scheduler.schedule(new?TimedSupervisorTask("ConnectMetaServer",?scheduler,?connectMetaServerExecutor,
????????????????????????metaServerConfig.getSchedulerConnectMetaServerTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerConnectMetaServerExpBackOffBound(),
????????????????????????()?->?metaClientExchanger.connectServer()),?metaServerConfig.getSchedulerConnectMetaServerFirstDelay(),
????????????????TimeUnit.SECONDS);

????????scheduler.schedule(
????????????????new?TimedSupervisorTask("CheckSessionNodeListChangePush",?scheduler,?checkNodeListChangePushExecutor,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(),
????????????????????????()?->?metaServerRegistry.pushNodeListChange(NodeType.SESSION)),
????????????????metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(),?TimeUnit.SECONDS);

????????scheduler.schedule(
????????????????new?TimedSupervisorTask("CheckDataNodeListChangePush",?scheduler,?checkNodeListChangePushExecutor,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(),
????????????????????????()?->?metaServerRegistry.pushNodeListChange(NodeType.DATA)),
????????????????metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(),?TimeUnit.SECONDS);

????????scheduler.schedule(new?TimedSupervisorTask("RaftClientRefresh",?scheduler,?raftClientRefreshExecutor,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushTimeout(),?TimeUnit.SECONDS,
????????????????????????metaServerConfig.getSchedulerCheckNodeListChangePushExpBackOffBound(),
????????????????????????()?->?raftExchanger.refreshRaftClient()),
????????????????metaServerConfig.getSchedulerCheckNodeListChangePushFirstDelay(),?TimeUnit.SECONDS);

????}??
}

6.4.2.1 啟動

ExecutorManager 的啟動設置是在 MetaServerBootstrap.initRaft 之中,分別啟動RaftServer,RaftClient,CliService。

private?void?initRaft()?{
????raftExchanger.startRaftServer(executorManager);
????raftExchanger.startRaftClient();
????raftExchanger.startCliService();
}

當 ?Raft 選出 ?Leader 之后,會調用到 ? ExecutorManager # startScheduler。

  • 首先生成各個ThreadPoolExecutor;
  • 然后運行本身的 各個TimedSupervisorTask,其會調用不同的handler,比如 ?connectServer,getSchedulerHeartbeatFirstDelay ?等等;

6.4.2.2 TimedSupervisorTask

TimedSupervisorTask 實現了 TimerTask。

public?class?TimedSupervisorTask?extends?TimerTask?{
????private?final?ScheduledExecutorService?scheduler;
????private?final?ThreadPoolExecutor???????executor;
????private?final?long?????????????????????timeoutMillis;
????private?final?Runnable?????????????????task;
????private?String?????????????????????????name;
????private?final?AtomicLong???????????????delay;
????private?final?long?????????????????????maxDelay;

????@Override
????public?void?run()?{
????????Future?future?=?null;
????????try?{
????????????future?=?executor.submit(task);
????????????//?block?until?done?or?timeout
????????????future.get(timeoutMillis,?TimeUnit.MILLISECONDS);
????????????delay.set(timeoutMillis);
????????}?catch?{
??????.....
????????}?finally?{
????????????if?(future?!=?null)?{
????????????????future.cancel(true);
????????????}
????????????scheduler.schedule(this,?delay.get(),?TimeUnit.MILLISECONDS);
????????}
????}
}

6.4.2.3 管理任務

可以看到管理任務大致有:

  • HeartbeatCheck :心跳檢測;
  • GetOtherDataCenterChange :查看其他數據中心的變化;
  • ConnectMetaServer :與其他的MetaServer交互;
  • CheckSessionNodeListChangePush :看看Session節點的變化;
  • CheckDataNodeListChangePush :查看數據節點變化;
  • RaftClientRefresh :看看Raft 服務消息;

TimedSupervisorTask 會定期執行,比如 ?CheckDataNodeListChangePush ?這個線程會定期執行 ?metaServerRegistry.pushNodeListChange(NodeType.DATA)) ?來看看是否有變化。這里就會用到DataNode注冊時候,Confirm的消息。

@Override
public?void?pushNodeListChange()?{
????NodeOperator?fireNode;if?((fireNode?=?dataConfirmStatusService.peekConfirmNode())?!=?null)?{
????????NodeChangeResult?nodeChangeResult?=?getNodeChangeResult();
????????Map>?map?=?nodeChangeResult.getNodes();
????????Map?addNodes?=?map.get(nodeConfig.getLocalDataCenter());if?(addNodes?!=?null)?{
????????????Map?previousNodes?=?dataConfirmStatusService.putExpectNodes(
????????????????fireNode.getNode(),?addNodes);if?(!previousNodes.isEmpty())?{
????????????????firePushDataListTask(fireNode,?nodeChangeResult,?previousNodes,?true);
????????????}
????????}
????????firePushSessionListTask(nodeChangeResult,?fireNode.getNodeOperate().toString());
????}
}

再比如定期去除過期的Node:

public?class?MetaServerRegistry?implements?Registry<Node>?{
????@Override
????public?void?evict()?{
????????for?(NodeType?nodeType?:?NodeType.values())?{
????????????StoreService?storeService?=?ServiceFactory.getStoreService(nodeType);
????????????if?(storeService?!=?null)?{
????????????????Collection?expiredNodes?=?storeService.getExpired();if?(expiredNodes?!=?null?&&?!expiredNodes.isEmpty())?{
????????????????????storeService.removeNodes(expiredNodes);
????????????????}
????????????}
????????}
????}??
}

6.4.3 ServiceFactory

ServiceFactory 需要特殊說明,它提供了系統所需要的一系列服務。特殊之處在于,ServiceFactory 不是由 MetaServerBootstrap 負責啟動,而是由 Spring 負責啟動。因為 ServiceFactory 繼承了ApplicationContextAware,所以啟動時候生成。

在Web應用中,Spring容器通常采用聲明式方式配置產生:開發者只要在web.xml中配置一個Listener,該Listener將會負責初始化Spring容器,MVC框架可以直接調用Spring容器中的Bean,無需訪問Spring容器本身。在這種情況下,容器中的Bean處于容器管理下,無需主動訪問容器,只需接受容器的依賴注入即可。

但在某些特殊的情況下,Bean需要實現某個功能,但該功能必須借助于Spring容器才能實現,此時就必須讓該Bean先獲取Spring容器,然后借助于Spring容器實現該功能。為了讓Bean獲取它所在的Spring容器,可以讓該Bean實現ApplicationContextAware接口。

下面代碼可以看出來,啟動了一系列服務。

public?class?ServiceFactory?implements?ApplicationContextAware?{
????private?static?Map???????storeServiceMap???=?new?HashMap<>();private?static?Map?connectManagerMap?=?new?HashMap<>();private?static?Map????????nodeServiceMap????=?new?HashMap<>();??
}
storeServiceMap?=?{HashMap@5107}??size?=?3
?{Node$NodeType@5525}?"SESSION"?->?{SessionStoreService@5526}?
??key?=?{Node$NodeType@5525}?"SESSION"
??value?=?{SessionStoreService@5526}?
?{Node$NodeType@4815}?"DATA"?->?{DataStoreService@5527}?
??key?=?{Node$NodeType@4815}?"DATA"
??value?=?{DataStoreService@5527}?
?{Node$NodeType@5528}?"META"?->?{MetaStoreService@5529}?
??key?=?{Node$NodeType@5528}?"META"
??value?=?{MetaStoreService@5529}?
connectManagerMap?=?{HashMap@5532}??size?=?3
?{Node$NodeType@5525}?"SESSION"?->?{SessionConnectionHandler@5548}?
??key?=?{Node$NodeType@5525}?"SESSION"
??value?=?{SessionConnectionHandler@5548}?
?{Node$NodeType@4815}?"DATA"?->?{DataConnectionHandler@5549}?
??key?=?{Node$NodeType@4815}?"DATA"
??value?=?{DataConnectionHandler@5549}?
?{Node$NodeType@5528}?"META"?->?{MetaConnectionHandler@5550}?
??key?=?{Node$NodeType@5528}?"META"
??value?=?{MetaConnectionHandler@5550}?
nodeServiceMap?=?{HashMap@5533}??size?=?3
?{Node$NodeType@5525}?"SESSION"?->?{SessionNodeServiceImpl@5540}?
??key?=?{Node$NodeType@5525}?"SESSION"
??value?=?{SessionNodeServiceImpl@5540}?
?{Node$NodeType@4815}?"DATA"?->?{DataNodeServiceImpl@5541}?
??key?=?{Node$NodeType@4815}?"DATA"
??value?=?{DataNodeServiceImpl@5541}?
?{Node$NodeType@5528}?"META"?->?{MetaNodeServiceImpl@5542}?
??key?=?{Node$NodeType@5528}?"META"
??value?=?{MetaNodeServiceImpl@5542}?

至此,MetaServer的架構和啟動介紹完成,我們下文將會介紹基本功能,比如注冊,存儲,續約等。

0xFF 參考

服務注冊中心 MetaServer 功能介紹和實現剖析 | SOFARegistry 解析

服務注冊中心如何實現 DataServer 平滑擴縮容 | SOFARegistry 解析

服務注冊中心數據一致性方案分析 | SOFARegistry 解析

服務注冊中心如何實現秒級服務上下線通知 | SOFARegistry 解析

服務注冊中心 Session 存儲策略 | SOFARegistry 解析

服務注冊中心數據分片和同步方案詳解 | SOFARegistry 解析

服務注冊中心 SOFARegistry 解析 | 服務發現優化之路

海量數據下的注冊中心 - SOFARegistry 架構介紹

服務端部署

客戶端使用

全面理解Raft協議

詳解螞蟻金服 SOFAJRaft | 生產級高性能 Java 實現

從JRaft來看Raft協議實現細節

SOFAJRaft—初次使用

JRaft 用戶指南 & API 詳解

怎樣打造一個分布式數據庫——rocksDB, raft, mvcc,本質上是為了解決跨數據中心的復制

sofa-bolt源碼閱讀(5)-日志

Raft 為什么是更易理解的分布式一致性算法

SOFAJRaft 源碼分析一(啟動流程和節點變化)

SOFAJRaft 實現原理 - 生產級 Raft 算法庫存儲模塊剖析

客戶端使用

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379002.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379002.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379002.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HttpService遠程校驗

今天學了下HttpService&#xff0c;和大家分享一下。HttpService是用來讀取遠程數據的一個對象&#xff0c;數據格式為XML。 我做了一個登陸校驗的功能&#xff0c;主要是通過HttpService將服務器端的用戶數據得到&#xff0c;然后在客戶端判斷輸入的用戶名和密碼是否存在。 主…

免費開源FTP Server軟件FileZilla Server

很多朋友在實際應用中都可能需要用到FTP Server類的軟件&#xff0c;這類軟件有很多&#xff0c;比較知名的有Serv&#xff0d;U、G6等&#xff0c;這里向大家介紹一下FileZilla Server&#xff0c;Windows平臺下一款不錯的FTP Server軟件&#xff0c;而且是免費的、開源的。 S…

Java BigDecimal floatValue()方法與示例

BigDecimal類floatValue()方法 (BigDecimal Class floatValue() method) floatValue() method is available in java.math package. floatValue()方法在java.math包中可用。 floatValue() method is used to convert a BigDecimal to a float value and when this BigDecimal m…

明明的隨機數(快排)

明明想在學校中請一些同學一起做一項問卷調查&#xff0c;為了實驗的客觀性&#xff0c;他先用計算機生成了N個1到1000之間的隨機整數&#xff08;N≤100&#xff09;&#xff0c;對于其中重復的數字&#xff0c;只保留一個&#xff0c;把其余相同的數去掉&#xff0c;不同的數…

ffplay分析 (seek操作處理)

《ffplay的數據結構分析》 《ffplay分析&#xff08;從啟動到讀取線程的操作&#xff09;》 《ffplay分析&#xff08;視頻解碼線程的操作&#xff09;》 《ffplay分析&#xff08;音頻解碼線程的操作&#xff09;》 《ffplay 分析&#xff08;音頻從Frame(解碼后)隊列取數據到…

android 代碼設置 鍵盤適應_硬核軟件,能在電腦上控制iPhone和Android手機

在電腦上控制手機大概已經不是什么新鮮操作&#xff0c;小米、華為都為自家手機和電腦的聯動推出了同屏操作之類的功能&#xff0c;此外也可以通過開源軟件Scrcpy來在Windows或者macOS上實現對安卓手機的控制&#xff0c;這些基本都只針對安卓手機。近期&#xff0c;奇客君發現…

網址出現error.aspx?aspxerrorpath=404.htm?aspxerrorpath=的原因及解決辦法轉

網址出現aspxerrorpath的問題描述 1.網頁打不開了,輸入網址后就提示error.aspx?aspxerrorpath/about-us.html&#xff0c;到底是什么原因啊&#xff1f; 2.ASP網站自定義了404錯誤頁&#xff0c;但訪問不存在的網址時網址錯誤頁后面總多出aspxerrorpath參數&#xff0c;怎么解…

ruby hash方法_Ruby中帶有示例的Hash.default(key = nil)方法

ruby hash方法Hash.default(key nil)方法 (Hash.default(keynil) Method) In this article, we will study about Hash.default(keynil) Method. The working of this method can be predicted with the help of its name but it is not as simple as it seems. Well, we will…

回文數、括號匹配(棧操作)

回文數 “xyzyx”是一個回文字符串&#xff0c;所謂回文字符串就是指正讀反讀均相同的字符序列&#xff0c;如“席主席”、“記書記”、“aha”和“ahaha”均是回文&#xff0c;但“ahah”不是回文。輸入一行字符&#xff08;僅包含小寫英文字母a~z&#xff09;請判斷這行字符…

ijkplayer 消息循環處理過程分析

ijkplayer 消息循環處理過程分析簡介一、消息隊列初始化1、 initWithContentURLString函數2、 ijkmp_ios_create函數3、 ijkmp_create函數二、消息隊列的消息循環處理函數啟動1、prepareToPlay函數2、ijkmp_prepare_async函數3、ijkmp_prepare_async_l函數4、ijkmp_msg_loop函數…

json解析對應的value為null_徒手擼一個JSON解析器

Java大聯盟致力于最高效的Java學習關注作者 | 田小波cnblogs.com/nullllun/p/8358146.html1、背景JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。相對于另一種數據交換格式 XML&#xff0c;JSON 有著諸多優點。比如易讀性更好&#xff0c;占用空間更少等。在 …

[一]設計模式初探

模式&#xff0c;顧名思義&#xff0c;就是做一種事情的方法歸納&#xff0c;就經驗來說&#xff0c;做什么事情有個好的方法來應對都是可以事半功倍的&#xff0c;在軟件開發中何謂好的模式? 我認為好的模式簡單來說就是保證你應對需求變化的時候不用做更多的代碼修改&#x…

Gentoo - ssh-agent配置

現在使用類似github這樣的service&#xff0c;一般來說都會配置ssh key認證。所以使用ssh-agent來管理私鑰就變的必要。在Gentoo下是這么配置的&#xff1a;- sudo emerge -avt keychain- 編輯.bashrc&#xff0c;加入keychain <private key 1 path> ... <private key…

java 方法 示例_Java ArrayDeque offerFirst()方法與示例

java 方法 示例ArrayDeque類offerFirst()方法 (ArrayDeque Class offerFirst() method) offerFirst() Method is available in java.lang package. offerFirst()方法在java.lang包中可用。 offerFirst() Method is used to add the given element at the front of this deque. …

平院Python習題

在讀寫文件之前&#xff0c;用于創建文件對象的函數是&#xff08; A &#xff09;。 A&#xff0e; open B&#xff0e; create C&#xff0e; file D&#xff0e;folder 解析&#xff1a; open(file, mode‘r’, buffering-1, encodingNone, errorsNone, newlineNone, close…

搭建srs服務器(rtmp)

搭建srs服務器&#xff08;rtmp&#xff09; 目錄:1、下載srs源碼&#xff08;從碼云上&#xff09;&#xff1a;2、 切換到srs.oschina&#xff1a;3、 這里使用3.0版本&#xff1a;4、 切換到trunk&#xff1a;5、 編譯&#xff1a;6、 啟動&#xff1a;7、查看日志輸出&…

MOSS信息管理策略定制(MOSS custom policies)

MOSS引入了信息管理策略&#xff0c;通過給文檔庫或列表附加一些策略可以實現一些自動化的功能&#xff0c;如自動給文檔打標簽&#xff0c;強制文檔的審核&#xff0c;啟用文檔的過期&#xff0c;甚至可以在用戶打印文檔時插入條形碼。難么&#xff0c;這么好用的功能是怎么實…

2560介紹_炒股高手收益翻10倍,只因妙用這一招2560戰法,看了都不虧了

(本文由公眾號越聲研究(yslc927yj)整理&#xff0c;僅供參考&#xff0c;不構成操作建議。如自行操作&#xff0c;注意倉位控制和風險自負。)選股是每個剛入市的新股民需掌握的技巧&#xff0c;通過資金選股了解資金流向&#xff0c;善用工具輕松選股&#xff0c;同時多留意行業…

二進制搜索樹_將排序的數組轉換為二進制搜索樹

二進制搜索樹Problem statement: 問題陳述&#xff1a; Given an array where elements are sorted in ascending order, convert it to a height balanced BST. 給定一個數組&#xff0c;其中元素按升序排序&#xff0c;請將其轉換為高度平衡的BST。 For this problem, a he…

rtmp協議分析(三次握手)

RTMP詳細分析(Message 消息&#xff0c;Chunk分塊) librtmp分析&#xff08;發送數據包處理&#xff09; librtmp分析&#xff08;接收數據包處理&#xff09; RTMP協議是Real Time Message Protocol(實時信息傳輸協議)的縮寫&#xff0c;它是由Adobe公司提出的一種應 用層的協…