小編給大家分享一下springcloud中Ribbon怎么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司專注于企業(yè)全網(wǎng)營(yíng)銷推廣、網(wǎng)站重做改版、紅寺堡網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5響應(yīng)式網(wǎng)站、商城網(wǎng)站開(kāi)發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為紅寺堡等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。
基本使用
這里使用基于zookeeper注冊(cè)中心+ribbon的方式實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶端負(fù)載均衡案例。
服務(wù)提供方
首先是一個(gè)服務(wù)提供方。代碼如下。
application.properties配置文件
spring.application.name=discovery-serviceserver.port=0service-B.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRulebootstrap.properties配置文件
spring.cloud.zookeeper.connect-string=192.168.0.15:2181引導(dǎo)程序,提供了一個(gè)ribbonService的rest接口服務(wù),注冊(cè)程序到zookeeper中。
@SpringBootApplication@EnableDiscoveryClient@RestControllerpublic class DiscoverClient { public static void main(String[] args) { SpringApplication.run(DiscoverClient.class, args); } @RequestMapping("/ribbonService") public String ribbonService(){ return "hello too ribbon"; }}
服務(wù)調(diào)用方
服務(wù)調(diào)用方就是進(jìn)行負(fù)載均衡的一方,利用ribbo的RestTemplate進(jìn)行負(fù)載調(diào)用服務(wù)。
RibbonConfig,配置ribbon的RestTemplate,通過(guò)@LoadBalanced注解實(shí)現(xiàn),具體原理稍后分析。
@Configurationpublic class RibbonConfig { /** * 實(shí)例化ribbon使用的RestTemplate * @return */ @Bean @LoadBalanced public RestTemplate rebbionRestTemplate(){ return new RestTemplate(); } /** * 配置隨機(jī)負(fù)載策略,需要配置屬性service-B.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule */ @Bean public IRule ribbonRule() { return new RandomRule(); }}
引導(dǎo)程序
@SpringBootApplication(scanBasePackages = "garine.learn.ribbon.loadblance")@EnableDiscoveryClient@RestControllerpublic class TestRibbonApplocation { public static void main(String[] args) { SpringApplication.run(TestRibbonApplocation.class, args); } @Autowired @LoadBalanced RestTemplate restTemplate; @GetMapping("/{applicationName}/ribbonService") public String ribbonService(@PathVariable("applicationName") String applicationName){ return restTemplate.getForObject("http://" + applicationName+"/ribbonService", String.class); }}
配置文件同上,服務(wù)名稱修改即可。
測(cè)試
啟動(dòng)兩個(gè)discovery-service,由于端口設(shè)置為0,所以是隨機(jī)端口。
啟動(dòng)服務(wù)調(diào)用方
瀏覽器訪問(wèn)服務(wù)調(diào)用方的提供的接口,路徑參數(shù)需要加上調(diào)用的服務(wù)名稱,例如http://localhost:8080/discovery-service/ribbonService,然后服務(wù)調(diào)用方使用ribbon的RestTemplate調(diào)用服務(wù)提供方的接口。
結(jié)果返回:hello too ribbon ,同時(shí)服務(wù)提供方啟動(dòng)的兩個(gè)服務(wù)都可能被調(diào)用,取決于怎么配置負(fù)載策略。
上面就是一個(gè)簡(jiǎn)單使用ribbon的例子,結(jié)合feign使用基本上是做類似上面所寫(xiě)的工作,那么ribbon到底是怎么實(shí)現(xiàn)的呢?
原理與源碼分析
ribbon實(shí)現(xiàn)的關(guān)鍵點(diǎn)是為ribbon定制的RestTemplate,ribbon利用了RestTemplate的攔截器機(jī)制,在攔截器中實(shí)現(xiàn)ribbon的負(fù)載均衡。負(fù)載均衡的基本實(shí)現(xiàn)就是利用applicationName從服務(wù)注冊(cè)中心獲取可用的服務(wù)地址列表,然后通過(guò)一定算法負(fù)載,決定使用哪一個(gè)服務(wù)地址來(lái)進(jìn)行http調(diào)用。
Ribbon的RestTemplate
RestTemplate中有一個(gè)屬性是List<ClientHttpRequestInterceptor> interceptors,如果interceptors里面的攔截器數(shù)據(jù)不為空,在RestTemplate進(jìn)行http請(qǐng)求時(shí),這個(gè)請(qǐng)求就會(huì)被攔截器攔截進(jìn)行,攔截器實(shí)現(xiàn)接口ClientHttpRequestInterceptor,需要實(shí)現(xiàn)方法是
ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException;
也就是說(shuō)攔截器需要完成http請(qǐng)求,并封裝一個(gè)標(biāo)準(zhǔn)的response返回。
ribbon中的攔截器
在Ribbon 中也定義了這樣的一個(gè)攔截器,并且注入到RestTemplate中,是怎么實(shí)現(xiàn)的呢?
在Ribbon實(shí)現(xiàn)中,定義了一個(gè)LoadBalancerInterceptor,具體的邏輯先不說(shuō),ribbon就是通過(guò)這個(gè)攔截器進(jìn)行攔截請(qǐng)求,然后實(shí)現(xiàn)負(fù)載均衡調(diào)用。
攔截器定義在org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.LoadBalancerInterceptorConfig#ribbonInterceptor
@Configuration@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig { @Bean //定義ribbon的攔截器 public LoadBalancerInterceptor ribbonInterceptor( LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) { return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); } @Bean @ConditionalOnMissingBean //定義注入器,用來(lái)將攔截器注入到RestTemplate中,跟上面配套使用 public RestTemplateCustomizer restTemplateCustomizer( final LoadBalancerInterceptor loadBalancerInterceptor) { return restTemplate -> { List<ClientHttpRequestInterceptor> list = new ArrayList<>( restTemplate.getInterceptors()); list.add(loadBalancerInterceptor); restTemplate.setInterceptors(list); }; }}
ribbon中的攔截器注入到RestTemplate
定義了攔截器,自然需要把攔截器注入到、RestTemplate才能生效,那么ribbon中是如何實(shí)現(xiàn)的?上面說(shuō)了攔截器的定義與攔截器注入器的定義,那么肯定會(huì)有個(gè)地方使用注入器來(lái)注入攔截器的。
在org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated方法里面,進(jìn)行注入,代碼如下。
@Configuration@ConditionalOnClass(RestTemplate.class)@ConditionalOnBean(LoadBalancerClient.class)@EnableConfigurationProperties(LoadBalancerRetryProperties.class)public class LoadBalancerAutoConfiguration { @LoadBalanced @Autowired(required = false) private List<RestTemplate> restTemplates = Collections.emptyList(); @Bean public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated( final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) { //遍歷context中的注入器,調(diào)用注入方法。 return () -> restTemplateCustomizers.ifAvailable(customizers -> { for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) { for (RestTemplateCustomizer customizer : customizers) { customizer.customize(restTemplate); } } }); } //...... }
遍歷context中的注入器,調(diào)用注入方法,為目標(biāo)RestTemplate注入攔截器,注入器和攔截器都是我們定義好的。
還有關(guān)鍵的一點(diǎn)是:需要注入攔截器的目標(biāo)restTemplates到底是哪一些?因?yàn)镽estTemplate實(shí)例在context中可能存在多個(gè),不可能所有的都注入攔截器,這里就是@LoadBalanced注解發(fā)揮作用的時(shí)候了。
LoadBalanced注解
嚴(yán)格上來(lái)說(shuō),這個(gè)注解是spring cloud實(shí)現(xiàn)的,不是ribbon中的,它的作用是在依賴注入時(shí),只注入實(shí)例化時(shí)被@LoadBalanced修飾的實(shí)例。
例如我們定義Ribbon的RestTemplate的時(shí)候是這樣的
@Bean @LoadBalanced public RestTemplate rebbionRestTemplate(){ return new RestTemplate(); }
因此才能為我們定義的RestTemplate注入攔截器。
那么@LoadBalanced是如何實(shí)現(xiàn)這個(gè)功能的呢?其實(shí)都是spring的原生操作,@LoadBalance的源碼如下
/** * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient * @author Spencer Gibb */@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Qualifierpublic @interface LoadBalanced {}
很明顯,‘繼承'了注解@Qualifier,我們都知道以前在xml定義bean的時(shí)候,就是用Qualifier來(lái)指定想要依賴某些特征的實(shí)例,這里的注解就是類似的實(shí)現(xiàn),restTemplates通過(guò)@Autowired注入,同時(shí)被@LoadBalanced修飾,所以只會(huì)注入@LoadBalanced修飾的RestTemplate,也就是我們的目標(biāo)RestTemplate。
攔截器邏輯實(shí)現(xiàn)
LoadBalancerInterceptor源碼如下。
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) { this.loadBalancer = loadBalancer; this.requestFactory = requestFactory; } public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) { // for backwards compatibility this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer)); } @Override public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException { final URI originalUri = request.getURI(); String serviceName = originalUri.getHost(); Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri); return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution)); }}
攔截請(qǐng)求執(zhí)行
@Overridepublic <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException { ILoadBalancer loadBalancer = getLoadBalancer(serviceId); //在這里負(fù)載均衡選擇服務(wù) Server server = getServer(loadBalancer); if (server == null) { throw new IllegalStateException("No instances available for " + serviceId); } RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));//執(zhí)行請(qǐng)求邏輯 return execute(serviceId, ribbonServer, request);}
我們重點(diǎn)看getServer方法,看看是如何選擇服務(wù)的
protected Server getServer(ILoadBalancer loadBalancer) { if (loadBalancer == null) { return null; } // return loadBalancer.chooseServer("default"); // TODO: better handling of key}
代碼配置隨機(jī)loadBlancer,進(jìn)入下面代碼
public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { return null; } else { try { //使用配置對(duì)應(yīng)負(fù)載規(guī)則選擇服務(wù) return rule.choose(key); } catch (Exception e) { logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e); return null; } }}
這里配置的是RandomRule,所以進(jìn)入RandomRule代碼
public Server choose(ILoadBalancer lb, Object key) { if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } //獲取可用服務(wù)列表 List<Server> upList = lb.getReachableServers(); List<Server> allList = lb.getAllServers(); //隨機(jī)一個(gè)數(shù) int serverCount = allList.size(); if (serverCount == 0) { /* * No servers. End regardless of pass, because subsequent passes * only get more restrictive. */ return null; } int index = rand.nextInt(serverCount); server = upList.get(index); if (server == null) { /* * The only time this should happen is if the server list were * somehow trimmed. This is a transient condition. Retry after * yielding. */ Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server;}
隨機(jī)負(fù)載規(guī)則很簡(jiǎn)單,隨機(jī)整數(shù)選擇服務(wù),最終達(dá)到隨機(jī)負(fù)載均衡。我們可以配置不同的Rule來(lái)實(shí)現(xiàn)不同的負(fù)載方式。
以上是“springcloud中Ribbon怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
分享題目:springcloud中Ribbon怎么用
網(wǎng)頁(yè)地址:http://muchs.cn/article2/ihciic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、品牌網(wǎng)站制作、企業(yè)建站、全網(wǎng)營(yíng)銷推廣、移動(dòng)網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)