<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ## 概述 在 Spring Cloud Netflix 階段我們采用 Eureka 做作為我們的服務注冊與發現服務器,現利用 Spring Cloud Alibaba 提供的 Nacos 組件替代該方案。 [Nacos 官網](https://nacos.io/zh-cn/) ## 什么是 Nacos Nacos是一款比較新的技術產品,在2018年7月開源,它致力于發現、配置和管理微服務,可以作為服務的注冊中心和配置中心。Nacos?提供了一組簡單易用的特性集,以“服務”為中心,能夠提供快速實現動態服務發現、服務配置、服務元數據及流量管理能力。 ![](https://img.kancloud.cn/d0/f9/d0f9f56b71851b3b1a25d1b4a8c68930_1130x495.png) ## Nacos數據模型 在Nacos的架構中有一個名字服務?(Naming?Service),提供了分布式系統中所有對象(Object)、實體(Entity)的“名字”到關聯的元數據之間的映射管理服務,解決了namespace到clusterId的路由問題。Nacos數據模型相對復雜,因為Nacos為服務的管理提供了很多特性,這些特性都被體現在數據的建模上。下面我們先來看看這張圖: ![](https://img.kancloud.cn/00/00/0000d2b3791f8043d23c46644f7e0ae5_1217x768.png) 服務層和實例層很好理解,因為在分布式中一個服務必然會有多個節點,但是Nacos在服務層與實例層中間新增了一個集群層,一個服務對應多個集群,一個集群對應多個實例。這個集群層帶來了以下收益: * Nacos的隔離性也從物理節點級別上升到了集群級別; * 在進行水平擴縮容的時候,也可以進行集群級別的伸縮; * 在應用需要使用Nacos時候,可以直接以集群為單位進行支撐。 實例分為兩種臨時實例和持久化實例: * 臨時實例:使用客戶端上報模式,需要能夠自動摘除不健康實例,并且不需要持久化,而上層的業務服務,例如微服務或者?Dubbo?服務,服務的?Provider?端支持添加匯報心跳的邏輯,就可以作為臨時實例進行注冊。 * 持久化實例:服務端來主動探測是否健康,因為客戶端不會上報心跳,那么自然就不能去自動摘除下線的實例。一些基礎的組件例如數據庫、緩存等,這些往往不能上報心跳,這種類型的服務在注冊時,就需要作為持久化實例注冊。 上面是從服務出發的數據模型。下面則是把視野放大,從不同環境出發,建立起更廣泛的數據模型,也可以理解為對不同環境的相同服務做了數據隔離。 ## 不同粒度的服務數據隔離 ![](https://img.kancloud.cn/e9/0d/e90db8791f0aa5fc13315fbde16d5cc3_485x401.png) 從命名空間到單個實例,中間經歷了組、服務、集群的層級劃分。每一個層級對應著不一樣的粒度的數據隔離,有效的滿足多業務多環境下的不同數據隔離需求。 這種圖要表達的意思很簡單,在Service層外面還包有兩層,分別是命名空間和組。 * 命名空間:業務在開發的時候可以將開發環境和生產環境分開,或者根據不同的業務線存在多個生產環境,命名空間常用場景之一就是不同環境的配置的區分隔離,例如開發測試環境和生產環境的資源(如配置、服務)隔離等。 * 組:對服務進行分組,可以滿足接口級別的隔離。 ## 一致性協議 Nacos作為服務的注冊中心,同樣也需要面對數據的一致性問題,也就是需要在CAP理論中選擇,在最新的版本中,Nacos支持AP原則和CP原則兩種原則。 Nacos中的CP原則實現是基于簡化的Raft,主要是一主多從策略,Raft有三類角色Leader(領袖)、Follower(群眾)以及Candidate(候選人)。Raft的選舉過程和上一小節將到的ZAB協議選舉過程是一樣的,也需要獲得半數以上的票才能夠成功選出Leader。有一點小的區別是,ZAB的Follower在投票給一個Leader之前必須和Leader的日志達成一致,而Raft的Follower則簡單地說是誰的term高就投票給誰。Raft?協議也和ZAB協議一樣強依賴?Leader?節點的可用性來確保集群數據的一致性。只有Leader節點才有權力領導寫操作。它的寫操作流程跟我在上一小節講到的ZAB協議流程一樣類似于二階段提交的流程。在心跳檢測中Raft協議的心跳是從Leader到Follower,?而ZAB協議則相反。Raft和ZAB如此相似,歸根結底是因為Raft和ZAB都是Paxos算法的簡化和優化,并且把Paxos更加的具象化,讓人易于實現和理解。 Nacos中的AP原則實現基于阿里自研協議?Distro。Distro?協議則是參考了內部?ConfigServer?和開源?Eureka。該實現沒有主從之分,當客戶端請求的某個節點掛了后,不會有類似于選主的過程,客戶端請求會自動切換到新的Nacos節點,當宕機的節點恢復后,又會重新回到集群管理內。所要做的就是同步一些新的服務注冊信息給重啟的Nacos節點,達到數據一致的效果。 在Nacos中如何切換AP原則和CP原則,主要切換的條件是注冊的服務實例是否是臨時實例。如果是臨時實例,則選用的是Nacos中的AP原則。 ## 服務注冊中心技術對比 ![](https://img.kancloud.cn/1d/f5/1df59f5920cc17caa24003cd64902ed9_1751x742.png) ## eureka與nacos的區別 * eureka采用AP模式實現注冊中心,無中心節點無leader概念,采用peertopeer集群架構 * nacos 默認采用AP模式,在1.0版本之后增加了AP+CP的混合模式實現注冊中心,采用raft協議,有leader概念 ## 安裝部署 ### 準備環境 Nacos 依賴 Java 環境來運行。如果您是從代碼開始構建并運行 Nacos,還需要為此配置 Maven 環境,請確保是在以下版本環境中安裝使用: * 64 bit OS,支持 Linux/Unix/Mac/Windows,推薦選用 Linux/Unix/Mac。 * 64 bit JDK 1.8+ ### 啟動 * windowd啟動方式 ``` D:\open-capacity-platform\register-center>h: H:\>cd alibaba H:\alibaba>cd open-capacity-platform H:\alibaba\open-capacity-platform>cd register-center H:\alibaba\open-capacity-platform\register-center>cd nacos-server H:\alibaba\open-capacity-platform\register-center\nacos-server>cd bin H:\alibaba\open-capacity-platform\register-center\nacos-server\bin>startup.cmd ``` * Linux啟動方式 ./startup.sh -m standalone ## 訪問服務 打開瀏覽器訪問:http://localhost:8848/nacos ![](https://img.kancloud.cn/1d/c1/1dc1245c9b1a77a0f9e19a0df0ddf181_1920x753.png) **注:從 0.8.0 版本開始,需要登錄才可訪問,默認賬號密碼為 nacos/nacos** ## nacos服務注冊,可參考user-ceneter 引入POM ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> ``` bootstrap.yml增加以下配置 ``` spring: cloud: nacos: discovery: server-addr: ip:prot #nacos的地址 ``` 工程啟動類添加以下注解 ``` @EnableDiscoveryClient ``` ## nacos API > 用API服務注冊,由于未心跳檢測,服務發現服務不會一直有效 ### 服務注冊API ![](https://img.kancloud.cn/a3/89/a389514090e691500b44458b9a472daa_1718x417.png) ### 服務發現API ![](https://img.kancloud.cn/b4/fd/b4fd86b96178a319748c26faf12b2f05_1708x702.png) ### 控制臺查看 ![](https://img.kancloud.cn/91/40/9140c967ac967f2ee003fb676e9784b2_1909x474.png) ## nacos源碼分析 ### nacos數據結構 ![](https://img.kancloud.cn/eb/94/eb942db612db49dfa908b8b005934b1b_1137x569.png) ### nacos 整體結構 ![](https://img.kancloud.cn/39/66/3966d5879d1cb084697fc3dc4c3ec6f2_954x766.png) ### 內核原理 通過NacosServiceRegistry實現了ServiceRegistry接口,ServiceInstance可以拿到當前注冊實例。 #### 注冊 nacos-client注冊 ``` @Override public void register(Registration registration) { if (StringUtils.isEmpty(registration.getServiceId())) { log.warn("No service to register for nacos client..."); return; } String serviceId = registration.getServiceId(); Instance instance = new Instance(); instance.setIp(registration.getHost()); instance.setPort(registration.getPort()); instance.setWeight(nacosDiscoveryProperties.getWeight()); instance.setClusterName(nacosDiscoveryProperties.getClusterName()); instance.setMetadata(registration.getMetadata()); try { namingService.registerInstance(serviceId, instance); log.info("nacos registry, {} {}:{} register finished", serviceId, instance.getIp(), instance.getPort()); } catch (Exception e) { log.error("nacos registry, {} register failed...{},", serviceId, registration.toString(), e); } } ``` #### 心跳 定時發送給nacos-server ``` package com.alibaba.nacos.client.naming.beat; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.net.NamingProxy; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import java.util.Map; import java.util.concurrent.*; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * @author harold */ public class BeatReactor { private ScheduledExecutorService executorService; private volatile long clientBeatInterval = 5 * 1000; private NamingProxy serverProxy; public final Map<String, BeatInfo> dom2Beat = new ConcurrentHashMap<String, BeatInfo>(); public BeatReactor(NamingProxy serverProxy) { this(serverProxy, UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT); } public BeatReactor(NamingProxy serverProxy, int threadCount) { this.serverProxy = serverProxy; executorService = new ScheduledThreadPoolExecutor(threadCount, new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(true); thread.setName("com.alibaba.nacos.naming.beat.sender"); return thread; } }); executorService.schedule(new BeatProcessor(), 0, TimeUnit.MILLISECONDS); } public void addBeatInfo(String serviceName, BeatInfo beatInfo) { NAMING_LOGGER.info("[BEAT] adding beat: {} to beat map.", beatInfo); dom2Beat.put(buildKey(serviceName, beatInfo.getIp(), beatInfo.getPort()), beatInfo); MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); } public void removeBeatInfo(String serviceName, String ip, int port) { NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", serviceName, ip, port); dom2Beat.remove(buildKey(serviceName, ip, port)); MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size()); } public String buildKey(String serviceName, String ip, int port) { return serviceName + Constants.NAMING_INSTANCE_ID_SPLITTER + ip + Constants.NAMING_INSTANCE_ID_SPLITTER + port; } class BeatProcessor implements Runnable { @Override public void run() { try { for (Map.Entry<String, BeatInfo> entry : dom2Beat.entrySet()) { BeatInfo beatInfo = entry.getValue(); if (beatInfo.isScheduled()) { continue; } beatInfo.setScheduled(true); executorService.schedule(new BeatTask(beatInfo), 0, TimeUnit.MILLISECONDS); } } catch (Exception e) { NAMING_LOGGER.error("[CLIENT-BEAT] Exception while scheduling beat.", e); } finally { executorService.schedule(this, clientBeatInterval, TimeUnit.MILLISECONDS); } } } class BeatTask implements Runnable { BeatInfo beatInfo; public BeatTask(BeatInfo beatInfo) { this.beatInfo = beatInfo; } @Override public void run() { long result = serverProxy.sendBeat(beatInfo); beatInfo.setScheduled(false); if (result > 0) { clientBeatInterval = result; } } } } ``` ### 功能說明 ServiceManager是nacos naming server中service核心管理類。在啟動時會執行一次本地信息到其他服務器,while(true)發生改變的service隊列內容進行本地更新,同時啟動對com.alibaba.nacos.naming.domains.meta.前綴的key的監聽;在運行過程中,執行controller接受的相關請求的功能,完成本地狀態和遠程服務器同步。 ## nacos流程圖 ![](https://img.kancloud.cn/da/df/dadf4b5214a7f5a62e9e13241d25c0b4_7146x6173.png) ## Nacos 注冊中心的心跳機制 與 Nacos 服務器之間的通信過程。在微服務啟動后每過5秒,會由微服務內置的 Nacos 客戶端主動向 Nacos 服務器發起心跳包(HeartBeat)。心跳包會包含當前服務實例的名稱、IP、端口、集群名、權重等信息。 ![](https://img.kancloud.cn/39/26/3926f8f22e304cf8bd1b56de55248556_1159x307.png) 如果你開啟微服務 Debug?日志,會清晰地看到每?5?秒一個心跳請求被發送到?Nacos 的 /nacos/v1/ns/instance/beat 接口,該請求會被 Nacos 服務器內置的 naming 模塊處理。 ``` 17:11:23.826 DEBUG 10720 --- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@665891d213 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=119.27.173.249 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: 6447aa06-9d70-41ea-83ef-cd27af1d3422}{Request-Module: Naming}{Host: 119.27.173.249:8848}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive}{Content-Length: 326} 17:11:28.837 DEBUG 10720 --- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@5f00479a12 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=119.27.173.249 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: 9fdf2264-9704-437f-bd34-7c9ee5e0be41}{Request-Module: Naming}{Host: 119.27.173.249:8848}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive} 17:11:38.847 DEBUG 10720 --- [ing.beat.sender] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@3521283812 pairs: {PUT /nacos/v1/ns/instance/beat?app=unknown&serviceName=DEFAULT_GROUP%40%40sample-service&namespaceId=public&port=9000&clusterName=DEFAULT&ip=119.27.173.249 HTTP/1.1: null}{Content-Type: application/x-www-form-urlencoded}{Accept-Charset: UTF-8}{Accept-Encoding: gzip,deflate,sdch}{Content-Encoding: gzip}{Client-Version: 1.3.2}{User-Agent: Nacos-Java-Client:v1.3.2}{RequestId: ccb6a586-897f-4036-9c0d-c614e2ff370a}{Request-Module: Naming}{Host: 119.27.173.249:8848}{Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2}{Connection: keep-alive} ``` naming 模塊在接收到心跳包后,會按下圖邏輯處理心跳包并返回響應: naming 模塊收到心跳包,首先根據 IP 與端口判斷 Nacos 是否存在該服務實例?如果實例信息不存在,在 Nacos 中注冊登記該實例。而注冊的本質是將新實例對象存儲在Map集合中; ![](https://img.kancloud.cn/90/d0/90d0c8a2998646f696fa8e86dc21f895_763x862.png) 如果實例信息已存在,記錄本次心跳包發送時間; * 設置實例狀態為“健康”; * 推送“微服務狀態變更”消息; * naming 模塊返回心跳包時間間隔。 那 Nacos 又是如何將無效實例從可用實例中剔除呢?Nacos Server 內置的邏輯是每過 20 秒對“實例 Map”中的所有“非健康”實例進行掃描,如發現“非健康”實例,隨即從Map中將該實例刪除。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看