亚洲精品中文免费|亚洲日韩中文字幕制服|久久精品亚洲免费|一本之道久久免费

<optgroup id="cczp1"><ruby id="cczp1"><cite id="cczp1"></cite></ruby></optgroup>
  • <acronym id="cczp1"></acronym>
    <acronym id="cczp1"><option id="cczp1"><ol id="cczp1"></ol></option></acronym>
    <delect id="cczp1"></delect>
    <center id="cczp1"></center>
    <delect id="cczp1"></delect><em id="cczp1"><button id="cczp1"><blockquote id="cczp1"></blockquote></button></em>
    1. <optgroup id="cczp1"><td id="cczp1"><dfn id="cczp1"></dfn></td></optgroup>

      SpringCloud系列-2Ribbon簡(jiǎn)介與應(yīng)用

      SpringCloud系列-2Ribbon簡(jiǎn)介與應(yīng)用

      學(xué)習(xí)目標(biāo)

    2. 理解負(fù)載均衡是概念,認(rèn)識(shí)常用負(fù)載均衡算法
    3. RestTemplate應(yīng)用
    4. Ribbon與其他負(fù)載均衡組件對(duì)比
    5. Ribbon集成springcloud
    6. 第1章:初識(shí)負(fù)載均衡

      負(fù)載均衡:建立在現(xiàn)有網(wǎng)絡(luò)結(jié)構(gòu)之上,它提供了一種廉價(jià)有效透明的方法擴(kuò)展網(wǎng)絡(luò)設(shè)備和服務(wù)器的帶寬、增加吞吐量、加強(qiáng)網(wǎng)絡(luò)數(shù)據(jù)處理能力、提高網(wǎng)絡(luò)的靈活性和可用性。

      負(fù)載均衡說(shuō)白了其實(shí)就是伴隨著微服務(wù)架構(gòu)的誕生的產(chǎn)物;過(guò)去的單體架構(gòu),前端頁(yè)面發(fā)起請(qǐng)求,然后后臺(tái)接收請(qǐng)求直接處理,這個(gè)時(shí)候不存在什么負(fù)載均衡;但是隨著單體架構(gòu)向微服務(wù)架構(gòu)的演變,每個(gè)后臺(tái)服務(wù)可能會(huì)部署在多臺(tái)服務(wù)器上面,這個(gè)時(shí)候頁(yè)面請(qǐng)求進(jìn)來(lái),到底該由哪臺(tái)服務(wù)器進(jìn)行處理呢?所以得有一個(gè)選擇,而這個(gè)過(guò)程就是負(fù)載均衡;同時(shí)選擇的方案有很多種,例如隨機(jī)挑選一臺(tái)或者一臺(tái)一臺(tái)輪著來(lái),這就是負(fù)載均衡算法。

      也可以通過(guò)例子來(lái)幫助自己記憶,就好比古代皇帝翻牌子,最開(kāi)始皇帝只有一個(gè)妃子,那不存在翻牌子這回事,再怎么翻也只能是這一個(gè)妃子侍寢。但是隨著妃子多了,就得有選擇了,不能同時(shí)讓所有妃子一起侍寢。

      1.1 實(shí)現(xiàn)方式

      1.1.1 HTTP重定向負(fù)載均衡

      工作原理圖如下:

      HTTP重定向服務(wù)器是一臺(tái)普通的應(yīng)用服務(wù)器,其唯一個(gè)功能就是根據(jù)用戶(hù)的HTTP請(qǐng)求計(jì)算出一臺(tái)真實(shí)的服務(wù)器地址,并將該服務(wù)器地址寫(xiě)入HTTP重定向響應(yīng)中返回給用戶(hù)瀏覽器。用戶(hù)瀏覽器在獲取到響應(yīng)之后,根據(jù)返回的信息,重新發(fā)送一個(gè)請(qǐng)求到真實(shí)的服務(wù)器上。DNS服務(wù)器解析到IP地址為192.168.8.74,即HTTP重定向服務(wù)器的IP地址。重定向服務(wù)器計(jì)根據(jù)某種負(fù)載均衡算法算出真實(shí)的服務(wù)器地址為192.168.8.77并返回給用戶(hù)瀏覽器,用戶(hù)瀏覽器得到返回后重新對(duì)192.168.8.77發(fā)起了請(qǐng)求,最后完成訪問(wèn)。

      這種負(fù)載均衡方案的優(yōu)點(diǎn)是比較簡(jiǎn)單,缺點(diǎn)是瀏覽器需要兩次請(qǐng)求服務(wù)器才能完成一次訪問(wèn),性能較差;同時(shí),重定向服務(wù)器本身的處理能力有可能成為瓶頸,整個(gè)集群的伸縮性規(guī)模有限;因此實(shí)踐中很少使用這種負(fù)載均衡方案來(lái)部署。

      1.1.2 DNS負(fù)載均衡

      DNS(Domain Name System)是因特網(wǎng)的一項(xiàng)服務(wù),它作為域名和IP地址相互映射的一個(gè)分布式數(shù)據(jù)庫(kù),能夠使人更方便的訪問(wèn)互聯(lián)網(wǎng)。人們?cè)谕ㄟ^(guò)瀏覽器訪問(wèn)網(wǎng)站時(shí)只需要記住網(wǎng)站的域名即可,而不需要記住那些不太容易理解的IP地址。在DNS系統(tǒng)中有一個(gè)比較重要的的資源類(lèi)型叫做主機(jī)記錄也稱(chēng)為A記錄,A記錄是用于名稱(chēng)解析的重要記錄,它將特定的主機(jī)名映射到對(duì)應(yīng)主機(jī)的IP地址上。如果你有一個(gè)自己的域名,那么要想別人能訪問(wèn)到你的網(wǎng)站,你需要到特定的DNS解析服務(wù)商的服務(wù)器上填寫(xiě)A記錄,過(guò)一段時(shí)間后,別人就能通過(guò)你的域名訪問(wèn)你的網(wǎng)站了。DNS除了能解析域名之外還具有負(fù)載均衡的功能,下面是利用DNS工作原理處理負(fù)載均衡的工作原理圖:

      由上圖可以看出,在DNS服務(wù)器中應(yīng)該配置了多個(gè)A記錄,如:www.woshuaiqi.com IN A 192.168.8.75;www.woshuaiqi.com IN A 192.168.8.76;www.woshuaiqi.com IN A 192.168.8.77;

      因此,每次域名解析請(qǐng)求都會(huì)根據(jù)對(duì)應(yīng)的負(fù)載均衡算法計(jì)算出一個(gè)不同的IP地址并返回,這樣A記錄中配置多個(gè)服務(wù)器就可以構(gòu)成一個(gè)集群,并可以實(shí)現(xiàn)負(fù)載均衡。上圖中,用戶(hù)請(qǐng)求www.woshuaiqi.com,DNS根據(jù)A記錄和負(fù)載均衡算法計(jì)算得到一個(gè)IP地址192.168.8.77,并返回給瀏覽器,瀏覽器根據(jù)該IP地址,訪問(wèn)真實(shí)的物理服務(wù)器192.168.8.77。所有這些操作對(duì)用戶(hù)來(lái)說(shuō)都是透明的,用戶(hù)可能只知道www.woshuaiqi.com這個(gè)域名。

      DNS域名解析負(fù)載均衡有如下優(yōu)點(diǎn):

    7. 將負(fù)載均衡的工作交給DNS,省去了網(wǎng)站管理維護(hù)負(fù)載均衡服務(wù)器的麻煩。
    8. 技術(shù)實(shí)現(xiàn)比較靈活、方便,簡(jiǎn)單易行,成本低,使用于大多數(shù)TCP/IP應(yīng)用。
    9. 對(duì)于部署在服務(wù)器上的應(yīng)用來(lái)說(shuō)不需要進(jìn)行任何的代碼修改即可實(shí)現(xiàn)不同服務(wù)器上的應(yīng)用訪問(wèn)。
    10. 服務(wù)器可以位于互聯(lián)網(wǎng)的任意位置。
    11. 同時(shí)許多DNS還支持基于地理位置的域名解析,即會(huì)將域名解析成距離用戶(hù)地理最近的一個(gè)服務(wù)器地址,這樣就可以加速用戶(hù)訪問(wèn),改善性能。
    12. 同時(shí),DNS域名解析也存在如下缺點(diǎn):

    13. 目前的DNS是多級(jí)解析的,每一級(jí)DNS都可能緩存A記錄,當(dāng)某臺(tái)服務(wù)器下線之后,即使修改了A記錄,要使其生效也需要較長(zhǎng)的時(shí)間,這段時(shí)間,DNS任然會(huì)將域名解析到已下線的服務(wù)器上,最終導(dǎo)致用戶(hù)訪問(wèn)失敗。
    14. 不能夠按服務(wù)器的處理能力來(lái)分配負(fù)載。DNS負(fù)載均衡采用的是簡(jiǎn)單的輪詢(xún)算法,不能區(qū)分服務(wù)器之間的差異,不能反映服務(wù)器當(dāng)前運(yùn)行狀態(tài),所以其的負(fù)載均衡效果并不是太好。
    15. .可能會(huì)造成額外的網(wǎng)絡(luò)問(wèn)題。為了使本DNS服務(wù)器和其他DNS服務(wù)器及時(shí)交互,保證DNS數(shù)據(jù)及時(shí)更新,使地址能隨機(jī)分配,一般都要將DNS的刷新時(shí)間設(shè)置的較小,但太小將會(huì)使DNS流量大增造成額外的網(wǎng)絡(luò)問(wèn)題。事實(shí)上,大型網(wǎng)站總是部分使用DNS域名解析,利用域名解析作為第一級(jí)負(fù)載均衡手段,即域名解析得到的一組服務(wù)器并不是實(shí)際提供服務(wù)的物理服務(wù)器,而是同樣提供負(fù)載均衡服務(wù)器的內(nèi)部服務(wù)器,這組內(nèi)部負(fù)載均衡服務(wù)器再進(jìn)行負(fù)載均衡,請(qǐng)請(qǐng)求發(fā)到真實(shí)的服務(wù)器上,最終完成請(qǐng)求。
    16. 1.1.3 反向代理負(fù)載均衡

      請(qǐng)求過(guò)程:

      用戶(hù)發(fā)來(lái)的請(qǐng)求都首先要經(jīng)過(guò)反向代理服務(wù)器,服務(wù)器根據(jù)用戶(hù)的請(qǐng)求要么直接將結(jié)果返回給用戶(hù),要么將請(qǐng)求交給后端服務(wù)器處理,再返回給用戶(hù)。

      反向代理負(fù)載均衡

      優(yōu)點(diǎn):

      • 隱藏后端服務(wù)器。與HTTP重定向相比,反向代理能夠隱藏后端服務(wù)器,所有瀏覽器都不會(huì)與后端服務(wù)器直接交互,從而能夠確保調(diào)度者的控制權(quán),提升集群的整體性能。
      • 故障轉(zhuǎn)移。與DNS負(fù)載均衡相比,反向代理能夠更快速地移除故障結(jié)點(diǎn)。當(dāng)監(jiān)控程序發(fā)現(xiàn)某一后端服務(wù)器出現(xiàn)故障時(shí),能夠及時(shí)通知反向代理服務(wù)器,并立即將其刪除。
      • 合理分配任務(wù) 。HTTP重定向和DNS負(fù)載均衡都無(wú)法實(shí)現(xiàn)真正意義上的負(fù)載均衡,也就是調(diào)度服務(wù)器無(wú)法根據(jù)后端服務(wù)器的實(shí)際負(fù)載情況分配任務(wù)。但反向代理服務(wù)器支持手動(dòng)設(shè)定每臺(tái)后端服務(wù)器的權(quán)重。我們可以根據(jù)服務(wù)器的配置設(shè)置不同的權(quán)重,權(quán)重的不同會(huì)導(dǎo)致被調(diào)度者選中的概率的不同。

      缺點(diǎn):

      • 調(diào)度者壓力過(guò)大 。由于所有的請(qǐng)求都先由反向代理服務(wù)器處理,那么當(dāng)請(qǐng)求量超過(guò)調(diào)度服務(wù)器的最大負(fù)載時(shí),調(diào)度服務(wù)器的吞吐率降低會(huì)直接降低集群的整體性能。
      • 制約擴(kuò)展。當(dāng)后端服務(wù)器也無(wú)法滿足巨大的吞吐量時(shí),就需要增加后端服務(wù)器的數(shù)量,可沒(méi)辦法無(wú)限量地增加,因?yàn)闀?huì)受到調(diào)度服務(wù)器的最大吞吐量的制約。

      1.2 常見(jiàn)算法

      1.2.1 輪詢(xún)

      Round Robin

      輪詢(xún)算法按照順序?qū)⑿碌恼?qǐng)求分配給下一個(gè)服務(wù)器,最終實(shí)現(xiàn)平分請(qǐng)求。

      1.優(yōu)點(diǎn):

      實(shí)現(xiàn)簡(jiǎn)單,無(wú)需記錄各種服務(wù)的狀態(tài),是一種無(wú)狀態(tài)的負(fù)載均衡策略。

      實(shí)現(xiàn)絕對(duì)公平

      2.缺點(diǎn):當(dāng)各個(gè)服務(wù)器性能不一致的情況,無(wú)法根據(jù)服務(wù)器性能去分配,無(wú)法合理利用服務(wù)器資源。

      代碼實(shí)現(xiàn)

      public class RoundRobinDemo { @Data public static class Server { private int serverId; private String name; private String ip; private int port; private int weight; public Server(int serverId, String name, String ip, int port) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; } public Server(int serverId, String name, String ip, int port, int weight) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; this.weight = weight; } @Override public String toString() { return “serverId=” + serverId + “, name='” + name + ”’ + “, ip='” + ip + ”’ + “, port=” + port + “, weight=” + weight; } } private static AtomicInteger NEXT_SERVER_COUNTER = new AtomicInteger(0); //輪詢(xún)算法的具體實(shí)現(xiàn) private static int select(int modulo) { for (; ; ) { int current = NEXT_SERVER_COUNTER.get(); int next = (current + 1) % modulo; boolean compareAndSet = NEXT_SERVER_COUNTER.compareAndSet(current, next); if (compareAndSet) { return next; } } } public static Server selectServer(List serverList) { return serverList.get(select(serverList.size())); } public static void main(String[] args) { List serverList = new ArrayList(); serverList.add(new Server(1, “服務(wù)器1″,”192.168.8.74”,8080)); serverList.add(new Server(2, “服務(wù)器2″,”192.168.8.75”,8080)); serverList.add(new Server(3, “服務(wù)器3″,”192.168.8.76”,8080)); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s", i + 1, selectedServer.toString()); } }}

      1.2.2 加權(quán)輪詢(xún)

      WeightedRound-Robin

      由于不同的服務(wù)器配置不同,因此它們處理請(qǐng)求的能力也不同,給配置高的服務(wù)器配置相對(duì)較高的權(quán)重,讓其處理更多的請(qǐng)求,給配置較低的機(jī)器配置較低的權(quán)重減輕期負(fù)載壓力。加權(quán)輪詢(xún)可以較好地解決這個(gè)問(wèn)題。

      1.思路

      根據(jù)權(quán)重的大小讓其獲得相應(yīng)被輪詢(xún)到的機(jī)會(huì)。

      服務(wù)器

      權(quán)重

      s1

      1

      s2

      2

      s3

      3

      可以根據(jù)權(quán)重我們?cè)趦?nèi)存中創(chuàng)建一個(gè)這樣的數(shù)組{s1,s2,s2,s3,s3,s3},然后再按照輪詢(xún)的方式選擇相應(yīng)的服務(wù)器。

      2.缺點(diǎn):請(qǐng)求被分配到三臺(tái)服務(wù)器上機(jī)會(huì)不夠平滑。前3次請(qǐng)求都不會(huì)落在server3上。

      Nginx實(shí)現(xiàn)了一種平滑的加權(quán)輪詢(xún)算法,可以將請(qǐng)求平滑(均勻)的分配到各個(gè)節(jié)點(diǎn)上。

      代碼實(shí)現(xiàn)

      public class WeightRoundRobin { @Data public static class Server { private int serverId; private String name; private String ip; private int port; private int weight; private int currentWeight; public Server(int serverId, String name, String ip, int port) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; } public Server(int serverId, String name, String ip, int port, int weight) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; this.weight = weight; } public void selected(int total) { this.currentWeight -= total; } public void incrCurrentWeight() { this.currentWeight += weight; } @Override public String toString() { return “serverId=” + serverId + “, name='” + name + ”’ + “, ip='” + ip + ”’ + “, port=” + port + “, weight=” + weight + “, currentWeight=” + currentWeight; } } //加權(quán)輪詢(xún)的核心邏輯 //每次選擇權(quán)重最大的那個(gè),被選擇之后,將當(dāng)前權(quán)重減去總權(quán)重,(算法怎么理解呢: // 理解成排隊(duì)去領(lǐng)獎(jiǎng),每次領(lǐng)完獎(jiǎng)就排到隊(duì)伍的最后繼續(xù)排,而排隊(duì)的總?cè)藬?shù)是總權(quán)重, // 全重是幾表示有多少個(gè)你的克?。? public static Server selectServer(List serverList) { int total = 0; Server selectedServer = null; int maxWeight = 0; for (Server server : serverList) { total += server.getWeight(); server.incrCurrentWeight(); //選取當(dāng)前權(quán)重最大的一個(gè)服務(wù)器 if (selectedServer == null || maxWeight < server.getCurrentWeight()) { selectedServer = server; maxWeight = server.getCurrentWeight(); } } if (selectedServer == null) { Random random = new Random(); int next = random.nextInt(serverList.size()); return serverList.get(next); } selectedServer.selected(total); return selectedServer; } public static void main(String[] args) { List serverList = new ArrayList(); serverList.add(new Server(1, "服務(wù)器1", "192.168.8.74", 8080, 1)); serverList.add(new Server(2, "服務(wù)器2", "192.168.8.75", 8080, 3)); serverList.add(new Server(3, "服務(wù)器3", "192.168.8.76", 8080, 10)); for (int i = 0; i < 10; i++) { Server server = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s", i + 1, server.toString()); } }}

      1.2.3 隨機(jī)

      Random

      思路:利用隨機(jī)數(shù)從所有服務(wù)器中隨機(jī)選取一臺(tái),可以用服務(wù)器數(shù)組下標(biāo)獲取。

      優(yōu)點(diǎn):使用簡(jiǎn)單;

      缺點(diǎn):不適合機(jī)器配置不同的場(chǎng)景

      代碼實(shí)現(xiàn)

      public class RandomLoadBalanceDemo { @Data public static class Server { private int serverId; private String name; private String ip; private int port; private int weight; public Server(int serverId, String name, String ip, int port) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; } public Server(int serverId, String name, String ip, int port, int weight) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; this.weight = weight; } @Override public String toString() { return “serverId=” + serverId + “, name='” + name + ”’ + “, ip='” + ip + ”’ + “, port=” + port + “, weight=” + weight; } } //輪詢(xún)算法的具體實(shí)現(xiàn) public static Server selectServer(List serverList) { Random selector = new Random(); int next = selector.nextInt(serverList.size()); return serverList.get(next); } public static void main(String[] args) { List serverList = new ArrayList(); serverList.add(new Server(1, “服務(wù)器1″,”192.168.8.74”,8080)); serverList.add(new Server(2, “服務(wù)器2″,”192.168.8.75”,8080)); serverList.add(new Server(3, “服務(wù)器3″,”192.168.8.76”,8080)); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s", i + 1, selectedServer.toString()); } }}

      1.2.4 加權(quán)隨機(jī)

      Weight Random

      思路:這里我們是利用區(qū)間的思想,通過(guò)一個(gè)小于在此區(qū)間范圍內(nèi)的一個(gè)隨機(jī)數(shù),選中對(duì)應(yīng)的區(qū)間(服務(wù)器),區(qū)間越大被選中的概率就越大。

      已知:

      服務(wù)器

      權(quán)重

      s1

      1

      s2

      2

      s3

      3

      s1:[0,1] s2:(1,3] s3 (3,6]

      代碼實(shí)現(xiàn)

      public class WeightRandomDemo { @Data public static class Server { private int serverId; private String name; private String ip; private int port; private int weight; public Server(int serverId, String name, String ip, int port) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; } public Server(int serverId, String name, String ip, int port, int weight) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; this.weight = weight; } @Override public String toString() { return “serverId=” + serverId + “, name='” + name + ”’ + “, ip='” + ip + ”’ + “, port=” + port + “, weight=” + weight; } } //算法的具體實(shí)現(xiàn) public static Server selectServer(List serverList) { int sumWeight = 0; for (Server server : serverList) { sumWeight += server.getWeight(); } Random serverSelector = new Random(); int nextServerRange = serverSelector.nextInt(sumWeight); int sum = 0; Server selectedServer = null; for (Server server : serverList) { if (nextServerRange >= sum && nextServerRange < server.getWeight() + sum) { selectedServer = server; } sum += server.getWeight(); } return selectedServer; } public static void main(String[] args) { List serverList = new ArrayList(); serverList.add(new Server(1, "服務(wù)器1","192.168.8.74",8080,1)); serverList.add(new Server(2, "服務(wù)器2","192.168.8.75",8080,5)); serverList.add(new Server(3, "服務(wù)器3","192.168.8.76",8080,10)); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s", i + 1, selectedServer.toString()); } }}

      1.2.5 Hash

      思路:根據(jù)每個(gè)每個(gè)請(qǐng)求ip(也可以是某個(gè)標(biāo)識(shí))ip.hash() % server.size()

      優(yōu)點(diǎn):將來(lái)自同一IP地址的請(qǐng)求,同一會(huì)話期內(nèi),轉(zhuǎn)發(fā)到相同的服務(wù)器;實(shí)現(xiàn)會(huì)話粘滯。

      缺點(diǎn):目標(biāo)服務(wù)器宕機(jī)后,會(huì)話會(huì)丟失;

      代碼實(shí)現(xiàn)

      public class IpHashDemo { @Data public static class Server { private int serverId; private String name; private String ip; private int port; public Server(int serverId, String name, String ip, int port) { this.serverId = serverId; this.name = name; this.ip = ip; this.port = port; } @Override public String toString() { return “serverId=” + serverId + “, name='” + name + ”’ + “, ip='” + ip + ”’ + “, port=” + port; } } //算法的具體實(shí)現(xiàn) public static Server selectServer(List serverList,String ip) { int ipHash = ip.hashCode(); return serverList.get(ipHash % serverList.size()); } public static void main(String[] args) { List serverList = new ArrayList(); serverList.add(new Server(1, “服務(wù)器1″,”192.168.8.74”,8080)); serverList.add(new Server(2, “服務(wù)器2″,”192.168.8.75”,8080)); serverList.add(new Server(3, “服務(wù)器3″,”192.168.8.76”,8080)); List ips = Arrays.asList(“192.168.8.74”, “192.168.8.75”, “192.168.8.76”); for (int i = 0; i < 10; i++) { for (String ip : ips) { Server selectedServer = selectServer(serverList, ip); System.out.format("請(qǐng)求ip:%s,選擇服務(wù)器%s", ip, selectedServer.toString()); } } }}

      1.2.6 最少鏈接

      思想:將請(qǐng)求分配到連接數(shù)最少的服務(wù)器上(目前處理請(qǐng)求最少的服務(wù)器)。

      優(yōu)點(diǎn):根據(jù)服務(wù)器當(dāng)前的請(qǐng)求處理情況,動(dòng)態(tài)分配;

      缺點(diǎn):算法實(shí)現(xiàn)相對(duì)復(fù)雜,需要監(jiān)控服務(wù)器請(qǐng)求連接數(shù);

      第2章:RestTemplate應(yīng)用

      2.1 RESTful架構(gòu)

      REST(Representational State Transfer)表象化狀態(tài)轉(zhuǎn)變(表述性狀態(tài)轉(zhuǎn)變),基于HTTP、URI、XML、JSON等標(biāo)準(zhǔn)和協(xié)議,支持輕量級(jí)、跨平臺(tái)、跨語(yǔ)言的架構(gòu)設(shè)計(jì)。是Web服務(wù)的一種新的架構(gòu)風(fēng)格(一種思想)。

      2.1.1 主要原則

      • 對(duì)網(wǎng)絡(luò)上所有的資源都有一個(gè)資源標(biāo)志符。
      • 對(duì)資源的操作不會(huì)改變標(biāo)識(shí)符。
      • 同一資源有多種表現(xiàn)形式(xml、json)
      • 所有操作都是無(wú)狀態(tài)的(Stateless)

      符合上述REST原則的架構(gòu)方式稱(chēng)為RESTful

      2.1.2 操作

      在Restful之前的操作:http://127.0.0.1/user/query/1 GET 根據(jù)用戶(hù)id查詢(xún)用戶(hù)數(shù)據(jù)http://127.0.0.1/user/save POST 新增用戶(hù)http://127.0.0.1/user/update POST 修改用戶(hù)信息http://127.0.0.1/user/delete/1 GET/POST 刪除用戶(hù)信息

      RESTful用法:http://127.0.0.1/user/1 GET 根據(jù)用戶(hù)id查詢(xún)用戶(hù)數(shù)據(jù)http://127.0.0.1/user POST 新增用戶(hù)http://127.0.0.1/user PUT 修改用戶(hù)信息http://127.0.0.1/user DELETE 刪除用戶(hù)信息

      之前的操作是沒(méi)有問(wèn)題的,大神認(rèn)為是有問(wèn)題的,有什么問(wèn)題呢?你每次請(qǐng)求的接口或者地址,都在做描述,例如查詢(xún)的時(shí)候用了query,新增的時(shí)候用了save,其實(shí)完全沒(méi)有這個(gè)必要,我使用了get請(qǐng)求,就是查詢(xún).使用post請(qǐng)求,就是新增的請(qǐng)求,我的意圖很明顯,完全沒(méi)有必要做描述,這就是為什么有了restful.

      http方法

      資源操作

      冪等

      安全

      GET

      SELECT

      POST

      INSERT

      PUT

      UPDATE

      DELETE

      DELETE

      冪等性:對(duì)同一REST接口的多次訪問(wèn),得到的資源狀態(tài)是相同的。

      安全性:對(duì)該REST接口訪問(wèn),不會(huì)使用服務(wù)器端資源的狀態(tài)發(fā)生改變。

      2.1.3 SpringMVC實(shí)現(xiàn)

      SpringMVC原生態(tài)的支持了REST風(fēng)格的架構(gòu)設(shè)計(jì)

      所涉及到的注解:

      —@RequestMapping—@PathVariable—@ResponseBody

      /** * @author Eclipse_2019 * @create 2022/2/8 18:04 */@RequestMapping(“restful/user”)@Controllerpublic class RestUserController { @Autowired private UserServiceImpl userService; /** * 根據(jù)用戶(hù)id查詢(xún)用戶(hù)數(shù)據(jù) * * @param id * @return */ @RequestMapping(value = “{id}”, method = RequestMethod.GET) @ResponseBody public ResponseEntity queryUserById(@PathVariable(“id”) Long id) { try { User user = this.userService.queryUserById(id); if (null == user) { // 資源不存在,響應(yīng)404 return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); } // 200 // return ResponseEntity.status(HttpStatus.OK).body(user); return ResponseEntity.ok(user); } catch (Exception e) { e.printStackTrace(); } // 500 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } /** * 新增用戶(hù) * * @param user * @return */ @RequestMapping(method = RequestMethod.POST) public ResponseEntity saveUser(@RequestBody User user) { try { this.userService.saveUser(user); return ResponseEntity.status(HttpStatus.CREATED).build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 500 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } /** * 更新用戶(hù)資源 * * @param user * @return */ @RequestMapping(method = RequestMethod.PUT) public ResponseEntity updateUser(@RequestBody User user) { try { this.userService.updateUser(user); return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } catch (Exception e) { e.printStackTrace(); } // 500 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); } /** * 刪除用戶(hù)資源 * * @param id * @return */ @RequestMapping(method = RequestMethod.DELETE) public ResponseEntity deleteUser(@RequestParam(value = “id”, defaultValue = “0”) Long id) { try { if (id.intValue() == 0) { // 請(qǐng)求參數(shù)有誤 return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } this.userService.deleteUserById(id); // 204 return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); } catch (Exception e) { e.printStackTrace(); } // 500 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); }}

      2.1.4 狀態(tài)碼

      GET安全且冪等獲取表示變更時(shí)獲取表示(緩存)200(OK) – 表示已在響應(yīng)中發(fā)出204(無(wú)內(nèi)容) – 資源有空表示301(Moved Permanently) – 資源的URI已被更新303(See Other) – 其他(如,負(fù)載均衡)304(not modified)- 資源未更改(緩存)400 (bad request)- 指代壞請(qǐng)求(如,參數(shù)錯(cuò)誤)404 (not found)- 資源不存在406 (not acceptable)- 服務(wù)端不支持所需表示500 (internal server error)- 通用錯(cuò)誤響應(yīng)503 (Service Unavailable)- 服務(wù)端當(dāng)前無(wú)法處理請(qǐng)求POST不安全且不冪等使用服務(wù)端管理的(自動(dòng)產(chǎn)生)的實(shí)例號(hào)創(chuàng)建資源創(chuàng)建子資源部分更新資源如果沒(méi)有被修改,則不過(guò)更新資源(樂(lè)觀鎖)200(OK)- 如果現(xiàn)有資源已被更改201(created)- 如果新資源被創(chuàng)建202(accepted)- 已接受處理請(qǐng)求但尚未完成(異步處理)301(Moved Permanently)- 資源的URI被更新303(See Other)- 其他(如,負(fù)載均衡)400(bad request)- 指代壞請(qǐng)求404 (not found)- 資源不存在406 (not acceptable)- 服務(wù)端不支持所需表示409 (conflict)- 通用沖突412 (Precondition Failed)- 前置條件失?。ㄈ鐖?zhí)行條件更新時(shí)的沖突)415 (unsupported media type)- 接受到的表示不受支持500 (internal server error)- 通用錯(cuò)誤響應(yīng)503 (Service Unavailable)- 服務(wù)當(dāng)前無(wú)法處理請(qǐng)求PUT不安全但冪等用客戶(hù)端管理的實(shí)例號(hào)創(chuàng)建一個(gè)資源通過(guò)替換的方式更新資源如果未被修改,則更新資源(樂(lè)觀鎖)200 (OK)- 如果已存在資源被更改201 (created)- 如果新資源被創(chuàng)建301(Moved Permanently)- 資源的URI已更改303 (See Other)- 其他(如,負(fù)載均衡)400 (bad request)- 指代壞請(qǐng)求404 (not found)- 資源不存在406 (not acceptable)- 服務(wù)端不支持所需表示409 (conflict)- 通用沖突412 (Precondition Failed)- 前置條件失敗(如執(zhí)行條件更新時(shí)的沖突)415 (unsupported media type)- 接受到的表示不受支持500 (internal server error)- 通用錯(cuò)誤響應(yīng)503 (Service Unavailable)- 服務(wù)當(dāng)前無(wú)法處理請(qǐng)求DELETE不安全但冪等刪除資源200 (OK)- 資源已被刪除301 (Moved Permanently)- 資源的URI已更改303 (See Other)- 其他,如負(fù)載均衡400 (bad request)- 指代壞請(qǐng)求404 (not found)- 資源不存在409 (conflict)- 通用沖突500 (internal server error)- 通用錯(cuò)誤響應(yīng)503 (Service Unavailable)- 服務(wù)端當(dāng)前無(wú)法處理請(qǐng)求

      2.2 RestTemplate

      官方文檔:https://docs.spring.io/spring-framework/docs/4.3.7.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html

      2.2.1 是什么

      傳統(tǒng)情況下在java代碼里訪問(wèn)restful服務(wù),一般使用Apache的HttpClient。不過(guò)此種方法使用起來(lái)太過(guò)繁瑣。spring提供了一種簡(jiǎn)單便捷的模板類(lèi)來(lái)進(jìn)行操作,這就是RestTemplate。

      2.2.2 使用

      定義一個(gè)簡(jiǎn)單的restful接口

      @RestControllerpublic class TestController{ @RequestMapping(value = “testPost”, method = RequestMethod.POST) public ResponseBean testPost(@RequestBody RequestBean requestBean) { ResponseBean responseBean = new ResponseBean(); responseBean.setRetCode(“0000”); responseBean.setRetMsg(“succ”); return responseBean; }}

      使用RestTemplate訪問(wèn)該服務(wù)

      //請(qǐng)求地址String url = “http://localhost:8080/testPost”;//入?yún)equestBean requestBean = new RequestBean();requestBean.setTest1(“1”);requestBean.setTest2(“2”);requestBean.setTest3(“3”);RestTemplate restTemplate = new RestTemplate();ResponseBean responseBean = restTemplate.postForObject(url, requestBean, ResponseBean.class);

      從這個(gè)例子可以看出,使用restTemplate訪問(wèn)restful接口非常的簡(jiǎn)單粗暴無(wú)腦。(url, requestMap, ResponseBean.class)這三個(gè)參數(shù)分別代表 請(qǐng)求地址、請(qǐng)求參數(shù)、HTTP響應(yīng)轉(zhuǎn)換被轉(zhuǎn)換成的對(duì)象類(lèi)型。

      RestTemplate方法的名稱(chēng)遵循命名約定,第一部分指出正在調(diào)用什么HTTP方法,第二部分指示返回的內(nèi)容。本例中調(diào)用了restTemplate.postForObject方法,post指調(diào)用了HTTP的post方法,Object指將HTTP響應(yīng)轉(zhuǎn)換為您選擇的對(duì)象類(lèi)型。

      RestTemplate定義了36個(gè)與REST資源交互的方法,其中的大多數(shù)都對(duì)應(yīng)于HTTP的方法。其實(shí),這里面只有11個(gè)獨(dú)立的方法,其中有十個(gè)有三種重載形式,而第十一個(gè)則重載了六次,這樣一共形成了36個(gè)方法。

      • delete() 在特定的URL上對(duì)資源執(zhí)行HTTP DELETE操作
      • exchange()在URL上執(zhí)行特定的HTTP方法,返回包含對(duì)象的ResponseEntity,這個(gè)對(duì)象是從響應(yīng)體中映射得到的
      • execute() 在URL上執(zhí)行特定的HTTP方法,返回一個(gè)從響應(yīng)體映射得到的對(duì)象
      • getForEntity() 發(fā)送一個(gè)HTTP GET請(qǐng)求,返回的ResponseEntity包含了響應(yīng)體所映射成的對(duì)象
      • getForObject() 發(fā)送一個(gè)HTTP GET請(qǐng)求,返回的請(qǐng)求體將映射為一個(gè)對(duì)象
      • postForEntity() POST 數(shù)據(jù)到一個(gè)URL,返回包含一個(gè)對(duì)象的ResponseEntity,這個(gè)對(duì)象是從響應(yīng)體中映射得到的
      • postForObject() POST 數(shù)據(jù)到一個(gè)URL,返回根據(jù)響應(yīng)體匹配形成的對(duì)象
      • headForHeaders() 發(fā)送HTTP HEAD請(qǐng)求,返回包含特定資源URL的HTTP頭
      • optionsForAllow() 發(fā)送HTTP OPTIONS請(qǐng)求,返回對(duì)特定URL的Allow頭信息
      • postForLocation() POST 數(shù)據(jù)到一個(gè)URL,返回新創(chuàng)建資源的URL
      • put() PUT 資源到特定的URL

      實(shí)際上,由于Post 操作的非冪等性,它幾乎可以代替其他的CRUD操作.

      第3章:Ribbon簡(jiǎn)介與應(yīng)用

      3.1 簡(jiǎn)介

      目前主流的負(fù)載方案分為以下兩種:

      • 集中式負(fù)載均衡,在消費(fèi)者和服務(wù)提供方中間使用獨(dú)立的代理方式進(jìn)行負(fù)載,有硬件的(比如 F5),也有軟件的(比如 Nginx)。
      • 客戶(hù)端自己做負(fù)載均衡,根據(jù)自己的請(qǐng)求情況做負(fù)載,Ribbon 就屬于客戶(hù)端自己做負(fù)載。

      Ribbon 是一個(gè)基于 HTTP和TCP的客戶(hù)端負(fù)載均衡工具。通過(guò) Spring Cloud 的封裝,可以讓我們輕松地將面向服務(wù)的 REST 模版請(qǐng)求自動(dòng)轉(zhuǎn)換成客戶(hù)端負(fù)載均衡的服務(wù)調(diào)用。

      Spring Cloud Ribbon 雖然只是一個(gè)工具類(lèi)框架,它不像服務(wù)注冊(cè)中心、配置中心、API 網(wǎng)關(guān)那樣需要獨(dú)立部署,但是它幾乎存在于每一個(gè) Spring Cloud 構(gòu)建的微服務(wù)和基礎(chǔ)設(shè)施中。因?yàn)槲⒎?wù)間的調(diào)用,API 網(wǎng)關(guān)的請(qǐng)求轉(zhuǎn)發(fā)等內(nèi)容,實(shí)際上都是通過(guò) Ribbon 來(lái)實(shí)現(xiàn)的(https://github.com/Netflix/ribbon)。

      Ribbon主要提供:

      • 客戶(hù)端負(fù)載均衡
      • 容錯(cuò)處理
      • 支持多協(xié)議的異步通信。支持HTTP、TCP、UDP協(xié)議。
      • 支持緩存和批量處理

      Ribbon模塊介紹:

      • ribbon: Ribbon功能應(yīng)用入口。使用Ribbon的功能可以通過(guò)初始化應(yīng)用入口,調(diào)用接口實(shí)現(xiàn)。該模塊依賴(lài)其他模版實(shí)現(xiàn)所需功能,比如容錯(cuò)處理ribbon依賴(lài)Histrix。
      • ribbon-loadbalancer:負(fù)載均衡功能入口。如果僅需要負(fù)載均衡功能,可以使用單獨(dú)使用該模塊。
      • ribbon-eureka:基于Eureka客戶(hù)端實(shí)現(xiàn)可用服務(wù)列表管理
      • ribbon-transport: 具備客戶(hù)端負(fù)載均衡能力的,基于RxNetty框架能夠支持HTTP、TCP、UDP協(xié)議的通信客戶(hù)端。
      • ribbon-httpclient: 具備客戶(hù)端負(fù)載均衡能力的,基于Apache HttpClient,支持REST風(fēng)格的HTTP請(qǐng)求客戶(hù)端。
      • ribbon-core: 客戶(hù)端配置APIs和其他共享APIs。
      • ribbon-example:使用例子。

      3.2 對(duì)比

      與Nginx的對(duì)比

      • Nginx是一種服務(wù)器端負(fù)載均衡 ,客戶(hù)端所有請(qǐng)求統(tǒng)一交給 nginx,由 nginx 進(jìn)行實(shí)現(xiàn)負(fù)載均衡請(qǐng)求轉(zhuǎn)發(fā)。
      • Ribbon是客戶(hù)端負(fù)載均衡 Ribbon 是從 eureka 注冊(cè)中心服務(wù)器端上獲取服務(wù)注冊(cè)信息列表,緩存到本地,然后在本地實(shí)現(xiàn)輪詢(xún)負(fù)載均衡策略。既在客戶(hù)端實(shí)現(xiàn)負(fù)載均衡。

      應(yīng)用場(chǎng)景的區(qū)別:

      • Nginx適合于服務(wù)器端實(shí)現(xiàn)負(fù)載均衡比如 Tomcat ,Ribbon適合與在微服務(wù)中RPC遠(yuǎn)程調(diào)用實(shí)現(xiàn)本地服務(wù)負(fù)載均衡,比如 Dubbo、SpringCloud 中都是采用本地負(fù)載均衡。
      • spring cloud的Netflix中提供了兩個(gè)組件實(shí)現(xiàn)軟負(fù)載均衡調(diào)用:ribbon和feign。
      • Ribbon是一個(gè)基于 HTTP 和 TCP 客戶(hù)端的負(fù)載均衡器,它可以在客戶(hù)端配置 ribbonServerList(服務(wù)端列表),然后輪詢(xún)請(qǐng)求以實(shí)現(xiàn)均衡負(fù)載。

      3.3 應(yīng)用

      不含Eureka

      1.先創(chuàng)建兩個(gè)服務(wù),用于負(fù)載均衡

      Server 1 和Server2 的端口號(hào)要不同,不然起不來(lái)

      Server 1接口如下:

      @RestControllerpublic class TestController { @GetMapping(“/user/{id}”) public String Info(@PathVariable Long id) { return “this is client1 ,id=” + id; }}

      Server 2接口如下:

      @RestControllerpublic class TestController { @GetMapping(“/user/{id}”) public String Info(@PathVariable Long id) { return “this is Client2,id=” + id; }}

      啟動(dòng)類(lèi)都是一樣的,如下:

      @SpringBootApplicationpublic class Application{ public static void main( String[] args ) { SpringApplication.run(Application.class, args); }}

      2.創(chuàng)建一個(gè)調(diào)用方來(lái)請(qǐng)求這個(gè)接口

      引依賴(lài)包

      org.springframework.cloud spring-cloud-commons 2.2.3.RELEASE org.springframework.cloud spring-cloud-starter-netflix-ribbon 2.2.3.RELEASE

      配置啟動(dòng)類(lèi),并注入 RestTemplate

      import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.web.client.RestTemplate;@EnableScheduling@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }}

      配置一下 application.properties,如下:

      ribbon-test.ribbon.listOfServers=127.0.0.1:2223,127.0.0.1:2222# ribbon連接超時(shí)default-test.ribbon.ConnectTimeout=500# ribbon讀超時(shí)default-test.ribbon.ReadTimeout=8000######## management ########management.security.enabled=falseendpoints.health.sensitive=false

      3.驗(yàn)證

      再創(chuàng)建一個(gè) 測(cè)試方法來(lái)驗(yàn)證是否生效,放在test 目錄下面,代碼如下:

      import com.test.Application;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.web.client.RestTemplate;@RunWith(SpringRunner.class)@SpringBootTest(classes = Application.class)public class RibbonBalanceClientTest { @Autowired private RestTemplate restTemplate; @Test public void contextLoads() { for (int i = 0; i < 10; i++) { String forObject = restTemplate.getForObject("http://ribbon-test/user/1000", String.class); System.out.println("============================"); System.out.println(forObject); } }}

      先啟動(dòng) 兩個(gè)server ,然后在 測(cè)試 測(cè)試類(lèi) ,結(jié)果如下:

      ============================this is Client2,id=1000============================this is client1 ,id=1000============================this is Client2,id=1000============================this is client1 ,id=1000============================this is Client2,id=1000============================this is client1 ,id=1000============================this is Client2,id=1000============================this is client1 ,id=1000============================this is Client2,id=1000============================this is client1 ,id=1000

      從結(jié)果可以看出實(shí)現(xiàn)了負(fù)載均衡,默認(rèn)是 輪詢(xún)策略,Client1和 clien2 依次調(diào)用。

      3.3.1 請(qǐng)求超時(shí)機(jī)制配置

      Ribbon 中有兩種和時(shí)間相關(guān)的設(shè)置,分別是請(qǐng)求連接的超時(shí)時(shí)間和請(qǐng)求處理的超時(shí)時(shí)間,設(shè)置規(guī)則如下:

      # 請(qǐng)求連接的超時(shí)時(shí)間ribbon.ConnectTimeout=2000 (默認(rèn)值:2000)# 請(qǐng)求處理的超時(shí)時(shí)間ribbon.ReadTimeout=5000 (默認(rèn)值:5000)# 也可以為每個(gè)Ribbon客戶(hù)端設(shè)置不同的超時(shí)時(shí)間, 通過(guò)服務(wù)名稱(chēng)進(jìn)行指定:goods-serviceo.ribbon.ConnectTimeout=2000goods-service.ribbon.ReadTimeout=5000

      3.3.2 并發(fā)連接數(shù)

      Ribbon可以通過(guò)下面的配置項(xiàng),來(lái)限制httpclient連接池的最大連接數(shù)量、以及針對(duì)不同host的最大連接數(shù)量。

      Ribbon底層的網(wǎng)絡(luò)通信,采用的是HttpClient中的PoolingHttpClientConnectionManager連接池,連接池的好處是避免頻繁建立連接(針對(duì)單個(gè)目標(biāo)地址)帶來(lái)的性能開(kāi)銷(xiāo),但是維護(hù)過(guò)多的鏈接會(huì)對(duì)客戶(hù)端造成內(nèi)存以及維護(hù)上的成本。

      所以,可以通過(guò)MaxTotalConnections限制總的連接數(shù)量,或者通過(guò)MaxConnectionsPerHost限制針對(duì)每個(gè)host的最大連接數(shù)。

      # 最大連接數(shù)ribbon.MaxTotalConnections=500 (默認(rèn)值:200)# 每個(gè)host最大連接數(shù)ribbon.MaxConnectionsPerHost=500 (默認(rèn)值:50)

      3.3.3 負(fù)載均衡配置

      負(fù)載均衡的核心,是通過(guò)負(fù)載均衡算法來(lái)實(shí)現(xiàn)對(duì)目標(biāo)服務(wù)請(qǐng)求的分發(fā)。Ribbion中默認(rèn)提供了7種負(fù)載均衡算法:

    17. BestAvailableRule,先過(guò)濾掉不可用的處于斷路器跳閘轉(zhuǎn)態(tài)的服務(wù),然后選擇一個(gè)并發(fā)量最小的服務(wù)。
    18. ZoneAvoidanceRule,默認(rèn)負(fù)載,復(fù)合判斷server所在區(qū)域的性能和server的可用性進(jìn)行服務(wù)的選擇。
    19. AvailabilityFilteringRule, 先過(guò)濾掉故障實(shí)例,再選擇并發(fā)量較小的實(shí)例
    20. RoundRobinRule,輪詢(xún)負(fù)載
    21. WeightedResponseTimeRule,每30秒計(jì)算一次服務(wù)器響應(yīng)時(shí)間,以響應(yīng)時(shí)間作為權(quán)重,響應(yīng)時(shí)間越短的服務(wù)器被選中的概率越大。
    22. ResponseTimeWeightedRule(已經(jīng)過(guò)期,改成了WeightedResponseTimeRule),
    23. RandomRule,隨機(jī)負(fù)載
    24. RetryRule,重試,先按RoundRobinRule進(jìn)行輪詢(xún),如果失敗就在指定時(shí)間內(nèi)進(jìn)行重試
    25. 如何指定Ribbon的負(fù)載策略呢?

      .ribbon.NFLoadBalancerRuleClassName: Should implement IRule(負(fù)載均衡算法)

      修改mall-portal項(xiàng)目中的application.properties文件,指定負(fù)載均衡算法。

      goods-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

      驗(yàn)證方法:

      1.在BaseLoadBalancer.chooseServer()方法中加斷點(diǎn)

      public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { //斷點(diǎn),查看rule所屬實(shí)例 return null; } else { }}

      2.在RandomRule.choose()方法增加斷點(diǎn),觀察請(qǐng)求是否進(jìn)入。

      除此之外,Ribbon還提供了自定義負(fù)載均衡的擴(kuò)展機(jī)制,只需要繼承AbstractLoadBalancerRule抽象類(lèi)即可。

      3.3.4 自定義負(fù)載均衡

      自定義負(fù)載均衡的實(shí)現(xiàn)主要分幾個(gè)步驟:

    26. 繼承AbstractLoadBalancerRule抽象類(lèi)
    27. 通過(guò)配置讓Ribbon使用自定義負(fù)載策略。
    28. public class DefineIpHashRule extends AbstractLoadBalancerRule { public Server choose(ILoadBalancer lb, Object key){ if(lb==null){ return null; }else { Server server = null; while (server == null) { //獲取可用的服務(wù)實(shí)例列表 List upList = lb.getReachableServers(); //獲取所有的服務(wù)實(shí)例列表 List allList = lb.getAllServers(); int serverCount = allList.size(); if (serverCount == 0) { return null; } int index=ipAddressHash(serverCount); server = upList.get(index); } return server; } } private int ipAddressHash(int serverCount){ ServletRequestAttributes requestAttributes=(ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); String remoteAddr=requestAttributes.getRequest().getRemoteAddr(); int code=Math.abs(remoteAddr.hashCode()); return code%serverCount; } @Override public Server choose(Object key) { return choose(getLoadBalancer(),key); } @Override public void initWithNiwsConfig(IClientConfig iClientConfig) { }}

      ILoadBalancer接口實(shí)現(xiàn)類(lèi)做了以下的一些事情:

      • 維護(hù)了存儲(chǔ)服務(wù)實(shí)例Server對(duì)象的二個(gè)列表。一個(gè)用于存儲(chǔ)所有服務(wù)實(shí)例的清單,一個(gè)用于存儲(chǔ)正常服務(wù)的實(shí)例清單
      • 初始化得到可用的服務(wù)列表,啟動(dòng)定時(shí)任務(wù)去實(shí)時(shí)的檢測(cè)服務(wù)列表中的服務(wù)的可用性,并且間斷性的去更新服務(wù)列表,結(jié)合注冊(cè)中心。
      • 選擇可用的服務(wù)進(jìn)行調(diào)用(這個(gè)一般交給IRule去實(shí)現(xiàn),不同的輪詢(xún)策略)

      修改application.properties文件

      spring-cloud-user-service.ribbon.NFLoadBalancerRuleClassName=com.mallportal.DefineIpHashRule

      3.3.5 Ribbon核心之Ping機(jī)制

      在ribbon負(fù)載均衡器中,提供了ping機(jī)制,每隔一段時(shí)間,就會(huì)去ping服務(wù)器,由 com.netflix.loadbalancer.IPing 接口去實(shí)現(xiàn)。

      單獨(dú)使用ribbon,不會(huì)激活ping機(jī)制,默認(rèn)采用DummyPing(在RibbonClientConfiguration中實(shí)例化),isAlive()方法直接返回true。

      ribbon和eureka集成,默認(rèn)采用NIWSDiscoveryPing(在EurekaRibbonClientConfiguration中實(shí)例化的),只有服務(wù)器列表的實(shí)例狀態(tài)為up的時(shí)候才會(huì)為Alive。

      IPing中默認(rèn)內(nèi)置了一些實(shí)現(xiàn)方法如下。

    29. PingUrl: 使用httpClient對(duì)目標(biāo)服務(wù)逐個(gè)實(shí)現(xiàn)Ping操作
    30. DummyPing: 默認(rèn)認(rèn)為對(duì)方服務(wù)是正常的,直接返回true
    31. NoOpPing:永遠(yuǎn)返回true
    32. 3.3.6 請(qǐng)求重試機(jī)制

      在網(wǎng)絡(luò)通信中,有可能會(huì)存在由網(wǎng)絡(luò)問(wèn)題或者目標(biāo)服務(wù)異常導(dǎo)致通信失敗,這種情況下我們一般會(huì)做容錯(cuò)設(shè)計(jì),也就是再次發(fā)起請(qǐng)求進(jìn)行重試。

      Ribbon提供了一種重試的負(fù)載策略:RetryRule,可以通過(guò)下面這個(gè)配置項(xiàng)來(lái)實(shí)現(xiàn)

      spring-cloud-user-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RetryRule

      由于在單獨(dú)使用Ribbon的機(jī)制下,并沒(méi)有開(kāi)啟Ping機(jī)制,所以所有服務(wù)默認(rèn)是認(rèn)為正常的,則這里并不會(huì)發(fā)起重試。如果需要演示重試機(jī)制,需要增加PING的判斷。

      1.引入依賴(lài)包

      org.apache.httpcomponents httpclient

      2.創(chuàng)建一個(gè)心跳檢查的類(lèi)

      public class HealthChecker implements IPing { @Override public boolean isAlive(Server server) { String urlStr = “http://”+server.getId()+”/healthCheck”; boolean isAlive = false; HttpClient httpClient = new DefaultHttpClient(); HttpUriRequest getRequest = new HttpGet(urlStr); try { HttpResponse response = httpClient.execute(getRequest); isAlive = response.getStatusLine().getStatusCode() == 200; }catch (Exception e){ }finally { getRequest.abort(); } return isAlive; }}

      3.修改mall-portal中application.properties文件,添加自定義心跳檢查實(shí)現(xiàn),以及心跳檢查間隔時(shí)間。

      goods-service.ribbon.NFLoadBalancerPingClassName=com.mallportal.HealthCheckergoods-service.ribbon.NFLoadBalancerPingInterval=3

      4.在goods-service這個(gè)模塊中,增加一個(gè)心跳檢查的接口

      @GetMapping(“/healthCheck”)public String health(){ return “SUCCESS”;}

      5.測(cè)試服務(wù)啟動(dòng)+停止,對(duì)于請(qǐng)求的影響變化。

      第4章:Loadbalancer

      參考官方文檔:https://spring.io/guides/gs/spring-cloud-loadbalancer

      LoadBalancer 是Spring Cloud自研的組件,支持WebFlux。

      由于Ribbon停止更新進(jìn)入維護(hù)狀態(tài),所以Spring Cloud不得不研發(fā)一套新的Loadbalancer機(jī)制進(jìn)行替代。

      在Spring Cloud 2020版本之后,Ribbon已經(jīng)被下掉了,直接用Loadbalancer取代,當(dāng)然我們?nèi)匀豢梢岳^續(xù)使用Ribbon。

      1.引入Loadbalancer相關(guān)jar包

      org.springframework.cloud spring-cloud-starter-loadbalancer org.springframework.boot spring-boot-starter-webflux

      2.定義一個(gè)配置類(lèi),這個(gè)配置類(lèi)通過(guò)硬編碼的方式寫(xiě)死了goods-service這個(gè)服務(wù)的實(shí)例列表,代碼如下

      @Configurationpublic class GoodsServiceConfiguration { @Bean ServiceInstanceListSupplier serviceInstanceListSupplier(){ return new GoodsServiceInstanceListSupplier(“goods-service”); }}//自定義實(shí)例列表class GoodsServiceInstanceListSupplier implements ServiceInstanceListSupplier{ private final String serviceId; GoodsServiceInstanceListSupplier(String serviceId){ this.serviceId=serviceId; } @Override public String getServiceId() { return serviceId; } @Override public Flux get() { //Flux.just可以指定序列中包含的全部元素。創(chuàng)建出來(lái)的 Flux 序列在發(fā)布這些元素之后會(huì)自動(dòng)結(jié)束 return Flux.just(Arrays.asList(new DefaultServiceInstance(serviceId+”1″,serviceId,”localhost”,9091,false), new DefaultServiceInstance(serviceId+”2″,serviceId,”localhost”,9081,false))); }}

      3.創(chuàng)建一個(gè)配置類(lèi),注入一個(gè)LoadBalancerClient

      @Configuration@LoadBalancerClient(name=”goods-service”,configuration = GoodsServiceConfiguration.class)public class WebClientConfiguration { @Bean @LoadBalanced WebClient.Builder webClientBuilder(){ return WebClient.builder(); }}

      4.修改測(cè)試類(lèi)

      @Slf4j@RestController@RequestMapping(“/order”)public class OrderController { @Autowired private WebClient.Builder loadBalancedWebClientBuilder; /** * 下單 * WebFlux需要用Mono或者Flux,它是WebFLux的核心。 */ @GetMapping public Mono order(){ log.info(“begin order”); return loadBalancedWebClientBuilder.build().get().uri(“http://goods-service/getGoodsById”).retrieve().bodyToMono(String.class); }}

      5.為了更好的看到效果,修改goods-service模塊,打印每個(gè)服務(wù)的端口號(hào)碼。

      @RestControllerpublic class GoodsController { @Value(“${server.port}”) private String port; @GetMapping(“/getGoodsById”) public String getGoodsById(){ System.out.println(“我是:”+port); return “返回商品詳細(xì)信息”; }}

      在Spring Cloud 2020.x版本中,Spring Cloud Netflix只留下了Eureka,其他的組件都已經(jīng)移除了。

      下文預(yù)告

    33. 推導(dǎo)出Ribbon的核心流程
    34. 通過(guò)源碼驗(yàn)證核心流程
    35. 鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
      用戶(hù)投稿
      上一篇 2022年6月19日 06:59
      下一篇 2022年6月19日 06:59

      相關(guān)推薦

      • ios手游模擬器(手游模擬器ios)

        本文主要講的是ios手游模擬器,以及和手游模擬器ios相關(guān)的知識(shí),如果覺(jué)得本文對(duì)您有所幫助,不要忘了將本文分享給朋友。 哪個(gè)iOS模擬器能多開(kāi)手游賬號(hào)?可以推薦個(gè)好用的模擬器給我嗎…

        2022年11月27日
      • 短視頻策劃內(nèi)容的3個(gè)要點(diǎn)(短視頻策劃內(nèi)容怎么做)

        短視頻在制作時(shí),內(nèi)容框架非常重要。如果直奔主題,然后結(jié)束,聚卓告訴你,這樣的短視頻已經(jīng)過(guò)時(shí)了。現(xiàn)在的短視頻需要框架的,但不是任何框架,它需要一種易于理解和消化的框架。而且,現(xiàn)在大多…

        2022年11月27日
      • 美團(tuán)第三季度實(shí)現(xiàn)營(yíng)收626億元,即時(shí)配送訂單量增至50億筆

        新京報(bào)訊(記者秦勝南)11月25日,美團(tuán)發(fā)布業(yè)績(jī)公告顯示,第三季度營(yíng)收為626億元,較去年同比增長(zhǎng)28.2%,凈利潤(rùn)為12.2億元。第三季度,美團(tuán)即時(shí)配送訂單數(shù)增長(zhǎng)至50億筆。截至…

        2022年11月27日
      • 個(gè)人怎么做抖音帶貨(個(gè)人做抖音帶貨能賺錢(qián)嗎)

        抖音如今是大家很熟悉的短視頻平臺(tái),不過(guò)現(xiàn)在的抖音卻不只是短視頻那么簡(jiǎn)單,它的功能非常豐富,其中一個(gè)就是可以帶貨,相信很多小伙伴都有在抖音上買(mǎi)過(guò)東西,抖音如今的變現(xiàn)能力也是不容小覷的…

        2022年11月25日
      • 小紅書(shū)平臺(tái)的一些機(jī)制及玩法詳解(小紅書(shū)玩法有哪些)

        關(guān)于小紅書(shū) 一:小紅書(shū)平臺(tái)的一些機(jī)制 1. 筆記內(nèi)容的CES評(píng)分機(jī)制 2. 筆記流量入口與長(zhǎng)尾效應(yīng) 二:小紅書(shū)優(yōu)質(zhì)筆記的特點(diǎn)(分維度、類(lèi)型分析) 1.筆記的本身架構(gòu)組成 維度 2.…

        2022年11月25日
      • 百度關(guān)鍵詞快速排名的4大原理解析(百度怎么刷關(guān)鍵詞)

        近期百度公告驚雷算法2.0,升級(jí)之快還是第一次吧,看來(lái)百度對(duì)于刷點(diǎn)擊行為是零容忍了。之前尹華峰SEO技術(shù)博客介紹過(guò)一篇如何使用刷點(diǎn)擊工具,其實(shí)市面上有很多這類(lèi)SEO快速排名的軟件,…

        2022年11月25日
      • OPPO Reno9 Pro+硬件規(guī)格強(qiáng) 搭載驍龍8+旗艦處理器

        OPPO Reno9系列正式發(fā)布,Reno9 Pro+作為三款新機(jī)中定位最高的超大杯機(jī)型,整體配置較上一代有著大幅度的升級(jí),如果單看硬件配置的話,Reno9 Pro+甚至是目前OP…

        2022年11月24日
      • 鬧劇落下帷幕,曼聯(lián)官宣 C 羅離隊(duì)

        1、鬧劇落下帷幕,曼聯(lián)官宣 C 羅離隊(duì) 在經(jīng)歷過(guò)半個(gè)賽季的激烈鬧劇、C 羅私自接受采訪炮轟曼聯(lián)之后,俱樂(lè)部終于做出了相對(duì)應(yīng)的措施:正式官宣 C 羅離隊(duì)。 在曼聯(lián)俱樂(lè)部發(fā)布的聲明中寫(xiě)…

        2022年11月24日
      • iqoo11什么時(shí)候上市 iqoo11發(fā)布時(shí)間最新消息

        iqoo11什么時(shí)候發(fā)布?隨著新一代旗艦芯片的發(fā)布,各家手機(jī)廠商也是公布了自己的旗艦機(jī),那么iqoo11什么時(shí)候發(fā)布呢?下面就讓小編為大家介紹一下,一起來(lái)看看吧。 iqoo11什么…

        2022年11月24日
      • 曝小米13系列已量產(chǎn):起步價(jià)格或定在4500元左右

        高通目前已經(jīng)發(fā)布第二代驍龍8芯片,首批機(jī)型已經(jīng)蓄勢(shì)待發(fā),小米此前也已經(jīng)宣布新旗艦要率先搭載。 據(jù)澎湃報(bào)道,小米13系列已經(jīng)正式量產(chǎn),全系均搭載4nm芯片,不出意外是標(biāo)配第二代驍龍8…

        2022年11月24日

      聯(lián)系我們

      聯(lián)系郵箱:admin#wlmqw.com
      工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息