文章插圖

文章插圖
1.什么是負載均衡
首先我們來看看維基百科對負載均衡的說明:
負載平衡(Load balancing)是一種計算機技術,用來在多個計算機(計算機集群)、網絡連接、CPU、磁盤驅動器或其他資源中分配負載,以達到最優化資源使用、最大化吞吐率、最小化響應時間、同時避免過載的目的 。使用帶有負載平衡的多個服務器組件,取代單一的組件,可以通過冗余提高可靠性 。負載平衡服務通常是由專用軟件和硬件來完成 。主要作用是將大量作業合理地分攤到多個操作單元上進行執行,用于解決互聯網架構中的高并發和高可用的問題 。舉個例子來解釋下負載均衡
下圖中,一群人在銀行排隊辦理業務,假設只有一個服務窗口,那么一個服務窗口來處理所有人員業務辦理,人少的時候,肯定是能夠辦理完的,如果人特別多的時候恩?一個服務窗口肯定是沒有辦法處理完這么多業務辦理的 。于是就出現如下圖這樣,一大堆人排隊 。
為什么?因為沒辦法保證讓某個人在某個窗口辦理業務 。大家可能還是會亂套(舉個極端例子:假設A窗口的小姐姐特別的漂亮,會不會她那里會有很多的男客戶排隊辦理業務恩?),因此銀行除了多開幾個窗口以外,還會設置一個排號,你拿到排號在哪個窗口,你就在哪個窗口進行辦理業務 。
2.注冊多個服務
在微服務Spring Cloud快速入門中,如果我們的商品服務一個不夠用的話,根據負載均衡的理論,那我們就可以多注冊幾個服務 。防止一個服務無法承載高并發時的情況 。
啟動注冊中心、商品服務,訂單服務,登錄注冊中心,查看我們當前的服務
2.1向注冊中心注冊多個服務
以同樣的項目代碼,設定端口為8091,其余配置均和之前的商品服務保持一致,然后啟動商品服務 。
server:port:8091#服務端口spring:application:name:hutao-microservice-item#指定服務名eureka:client:registerWithEureka:true#是否將自己注冊到Eureka服務中,默認為truefetchRegistry:true#是否從Eureka中獲取注冊信息,默認為trueserviceUrl:#Eureka客戶端與Eureka服務端進行交互的地址defaultZone:http://127.0.0.1:9090/eureka/instance:prefer-ip-address:true#將自己的ip地址注冊到Eureka服務中點擊如下所示,可以看到我們啟動了1個注冊中心,1個訂單服務,2個商品服務2.2通過服務ID找到服務
在微服務Spring Cloud快速入門中我們定義了如下Feign接口,在這個接口上面,[email protected],指定了服務ID:hutao-microservice-item 。這個Feign接口會給我發起Http調用 。當然在這里我們可能無法看到負載均衡的效果,因此我們需要稍微深入下底層代碼 。
@FeignClient(value="http://www.mnbkw.com/jxjc/188204/hutao-microservice-item")@RequestMapping("/itemservice")[email protected]:使用聲明式HTTP客戶端發起請求:[email protected][email protected][email protected][email protected][email protected](value="http://www.mnbkw.com/jxjc/188204/item/{itemId}")ItemsqueryItem(@PathVariable("itemId")StringitemId);}大家應該還記得,我們的商品服務和訂單服務,注冊到注冊中心的時候,我們在啟動類上面添加了一個注解:@EnableDiscoveryClient 。現在我們來看下DiscoveryClient這個接口
2.2.1.DiscoveryClient 解讀
DiscoveryClient接口源碼如下,他是用來發現服務的,比如發現Netflix服務,其中有個方法List getInstances(String serviceId)是根據服務ID獲取服務實例集合 。
/***Representsreadoperationscommonlyavailabletodiscoveryservicess[email protected][email protected][email protected]thediscoveryclient.*/intDEFAULT_ORDER=0;/***Ahuman-readabledescriptionoftheimplementation,[email protected]*/Stringdescription();[email protected][email protected]*/List<ServiceInstance>getInstances(StringserviceId);[email protected]*/List<String>getServices();[email protected][email protected](){returnDEFAULT_ORDER;}}我們可以看到DiscoveryClient 有4個實現,當然這里我們用的肯定是Eureka 。現在我們來使用下DiscoveryClient 這個接口
首先把這個接口依賴注入到我們的Controller中,我們看下能獲取到什么
@AutowiredprivateDiscoveryClientdiscoveryClient;@GetMapping(value="http://www.mnbkw.com/jxjc/188204/order/{orderId}")publicOrderqueryOrderById(@PathVariable("orderId")StringorderId){StringserviceId="hutao-microservice-item";List<ServiceInstance>instances=discoveryClient.getInstances(serviceId);System.out.println(instances);returnnull;}通過調試代碼,我們發現如我們之前所說,我們可以通過服務ID:hutao-microservice-item,找到兩個服務實例 。2.2.3.從服務實例中獲取服務信息,發起Http請求
那么這個時候,我們怎么去發起請求恩?很簡單,就是從服務實例中,獲取到服務信息后,將接口的請求地址拼接出來 。然后使用restTemplate發起Http請求
@AutowiredprivateRestTemplaterestTemplate;@GetMapping(value="http://www.mnbkw.com/jxjc/188204/order/{orderId}")publicOrderqueryOrderById(@PathVariable("orderId")StringorderId){StringserviceId="hutao-microservice-item";List<ServiceInstance>instances=discoveryClient.getInstances(serviceId);ServiceInstanceserviceInstance=instances.get(0);Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();Itemsitems=restTemplate.getForObject("http://"+url+"/itemservice/item/1",Items.class);System.out.println(items);returnnull;}可以看到我們的請求是能正常訪問的 。當然也有問題存在,那就是我們拿到的是多個服務,程序怎么知道我要調用的是哪一個服務呢?上述案例中,我們獲取到的實例是兩個,那么每次調用的時候,我們怎么來確定,我要調用的是哪一個服務?,因為這時候我們拿到的兩個服務,也就是兩個不同的IP地址,這時候就需要一個負載均衡器來幫我們選擇一個IP進行訪問 。
在Spring Cloud中,netfix提供一個負載均衡器Ribbon,該負載均衡器是聲明式的,其用法如下所示,[email protected]Balanced,這時候我們的RestTemplate就具備了負載均衡的功能 。
注意:RestTemplate底層默認使用的jdk的標準實現,如果我們想讓RestTemplate的底層使用okhttp,可以替換實現的 。如下源碼所示,RestTemplate提供了三個構造方法 。
@[email protected]@LoadBalancedpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}在上面的一個案例中,我們通過從服務實例中,獲取到服務,然后再從服務中心獲取具體的IP地址信息,發起請求 。Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();Itemsitems=restTemplate.getForObject("http://"+url+"/itemservice/item/1",但是,現在我們不需要這么做了,因為我們對restTemplate聲明了是需要負載均衡的,因此我們發起請求的時候,就不需要指定IP地址了,我們可以用服務ID來代替IP地址,然后由restTemplate來幫我選擇需要調用的IP 。因此上述代碼會被簡化為如下所示,被注釋掉的代碼就是被優化的代碼 。@GetMapping(value="http://www.mnbkw.com/jxjc/188204/order/{orderId}")publicOrderqueryOrderById(@PathVariable("orderId")StringorderId){StringserviceId="hutao-microservice-item";//List<ServiceInstance>instances=discoveryClient.getInstances(serviceId);//ServiceInstanceserviceInstance=instances.get(0);//Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();//Itemsitems=restTemplate.getForObject("http://"+url+"/itemservice/item/1",Items.class);Itemsitems=restTemplate.getForObject("http://"+serviceId+"/itemservice/item/1",Items.class);System.out.println(items);returnnull;}重啟服務后,商品服務仍然可用,那么這時候怎么來驗證我們的負載均衡成功了?2.2.5.簡單驗證負載均衡
其實最簡單的驗證方式就是,在商品服務中,添加日志,看看哪個服務記錄了日志或者debug調試,看走哪一個服務的代碼 。即可驗證我們的負載均衡是否成功 。
當然這里我們就不做上述方式的演示,來做一點高難度的代碼分析 。
[email protected]te源碼解析
1、首先看org.springframework.web.client.RestTemplate類 。
當我們執行如下代碼時
restTemplate.getForObject("http://"+serviceId+"/itemservice/item/1",Items.class);最終會執行到如下方法doExecute(URI,HttpMethod,RequestCallback,ResponseExtractor<T>)逐步深入代碼,我們找到了如下的攔截器 。LoadBalancerInterceptororg.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor
繼續深入代碼,我們找到了RibbonLoadBalancerClient這個類org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient通過查看代碼,我們發現了getLoadBalancer(String)這個方法通過serviceId找到了兩個服務實例,
protectedServergetServer(ILoadBalancerloadBalancer,Objecthint){if(loadBalancer==null){returnnull;}//Use'default'onanullhint,orjustpassiton?returnloadBalancer.chooseServer(hint!=null?hint:"default");}4.負載均衡代碼驗證演示
通過上述源碼分析,我們發現如果我們開啟了負載均衡,但是沒有配置負載均衡參數,則會采用默認的配置,也就是輪詢算法來實現負載均衡 。
通過上述的debug閱讀,我們參照getServer這個方法來寫代碼測試下負載均衡
@AutowiredprivateLoadBalancerClientloadBalancerClient;@GetMapping(value="http://www.mnbkw.com/jxjc/188204/order/{orderId}")publicOrderqueryOrderById(@PathVariable("orderId")StringorderId){StringserviceId="hutao-microservice-item";for(inti=0;i<10;i++){ServiceInstancechoose=loadBalancerClient.choose(serviceId);System.out.println(choose);}//List<ServiceInstance>instances=discoveryClient.getInstances(serviceId);//ServiceInstanceserviceInstance=instances.get(0);//Stringurl=serviceInstance.getHost()+":"+serviceInstance.getPort();//Itemsitems=restTemplate.getForObject("http://"+serviceId+"/itemservice/item/1",Items.class);//System.out.println(items);returnnull;}在我們之前調試代碼的時候我們發現源碼中定義了IRule.choose(Object objet)這個接口,并且如下幾個實現 。
比如,我們設置為隨機策略 。
hutao-microservice-item是我們需要對某個服務設定NFLoadBalancerRuleClassName是我們的策略(也就是實現類的路徑)
【負載均衡服務器有哪些 服務器負載均衡器】
hutao-microservice-item:ribbon:NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule重啟服務后,再看我們的負載均衡,是隨機的,而不是輪詢的 。- 免費使用的服務器 服務器免費用
- 企業郵件服務器租用 租用郵政信箱
- ftp上傳文件失敗原因 ftp服務器傳輸數據失敗的原因
- linux重啟apache服務器命令 怎么重啟apache服務器
- 浪潮服務器ipmi默認ip 浪潮ipmi地址配置
- 云主機服務器哪里好 云主機性價比
- 代理服務器網址設置 上網設置代理服務器
- 如何把文件上傳服務器 上傳文件到服務器命令
- 虛擬主機和云服務器的區別 虛擬主機和服務器有什么區別
- 怎么開通地圖服務 自己搭建地圖服務器
