本文转载自微信公众号“Java中文社区”,作者雷哥。转载本文请联系Java中文社区公众号。在Nacos中,服务调用主要是通过RestTemplate+Ribbon实现的。RestTemplate是Spring提供的Restful请求实现类,Ribbon是客户端负载均衡器。通过Ribbon可以获取服务实例的具体信息(IP和端口号)。之后,通过RestTemplate添加服务实例的具体信息即可完成一次服务调用。RestTemplate+Ribbon调用服务的实现方式有两种:通过代码调用服务和通过注解调用服务。但是两种实现的原理是一样的:通过注册中心拉取可用服务列表到本地(客户端),然后通过客户端负载均衡器获取某台服务器的具体信息,然后服务器request就是这样,如下图:1.代码方法调用通过代码调用服务在实际工作中并不常用,主要是写法太麻烦,但是理解对理解注解调用方法很有帮助后面,所以我们这里重点说一下。服务调用需要两个角色:一个是服务提供者(Provider),一个是服务调用者(Consumer)。接下来,让我们创建这两个角色。1.1创建服务提供者:Provider第一步:首先创建一个SpringBoot项目(SpringCloud项目是基于SpringBoot创建的),添加spring-web和nacos-discovery依赖。具体依赖信息如下:org.springframework.bootspring-boot-starter-webcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discovery第二步:设置Nacos相关配置,添加如下配置inapplication.yml:spring:application:name:springcloud-nacos-provider#项目名(nacos注册的服务名)cloud:nacos:discovery:username:nacos#nacos登录用户名password:nacos666#nacos密码server-addr:127.0.0.1:8848#nacos服务器地址server:port:8081#项目启动端口号第三步:添加服务方法,如下代码所示:importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;导入org.springframework.web.bind.annotation.PathVariable;导入org.springframework.web.bind.annotation.RequestMapping;导入org.springframework.web.bind.annotation.RestController;@SpringBootApplication@RestController公共类HttpProviderApplication{publicstaticvoidmain(String[]args){SpringApplication.运行(HttpProviderApplication.class,参数);}/***为客户端提供一个可调用的接口*/@RequestMapping("/call/{name}")publicStringcall(@PathVariableStringname){return"我是提供者。收到一条消息来自:"+姓名;}}然后用同样的方法再创建两个服务提供者,最终对应的端口号为:127.0.0.1:8081127.0.0.1:8082127.0.0.1:8083这三个服务提供者打印的内容是“我是Provider...”,“我是Provider2...”,“我是Provider3...”,如下图所示:1.2创建服务调用作者:Consumer本文的核心是实现代码服务调用者,其创建方法与服务提供者类似第一步:创建一个SpringBoot项目,添加spring-web和nacos-discovery依赖。具体依赖如下:org.springframework.bootspring-boot-starter-webcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discovery可能有人会有疑问。本文标题为SpringCloudAlibabaNacos+Ribbon,为什么不添加Ribbon依赖呢?这是因为SpringCloudAlibabaNacos中已经内置了Ribbon框架。打开项目的依赖树可以清楚的看到,如下图:Step2:设置Nacos相关配置,在application.yml中添加如下配置:spring:application:name:springcloud-nacos-consumer#项目名称(nacos注册的服务名称)cloud:nacos:discovery:username:nacos#nacos登录用户名密码:nacos666#nacos密码server-addr:127.0.0.1:8848#nacos服务器地址server:port:8091#项目启动端口第三步:在项目启动类中,使用SpringJavaConfig声明RestTemplate对象,如下代码所示:importorg.springframework.boot.SpringApplication;导入org.springframework.boot.autoconfigure.SpringBootApplication;导入org.springframework.context.annotation.Bean;导入org.springframework.web.client.RestTemplate;@SpringBootApplication公共类RibbonCodeConsumerApplication{publicstaticvoidmain(String[]args){SpringApplication.run(RibbonCodeConsumerApplication.class,args);}/***使用SpringJavaConfig声明RestTemplate*/@BeanRestTemplaterestTemplate(){returnnewRestTemplate();}}第四步:使用RestTemplate+Ribbon的代码方法调用服务。首先,使用Ribbon提供的LoadBalancerClient对象的choose方法,根据Nacos中的serviceid获取健康的服务实例。服务实例中包含服务的IP地址和端口号,然后使用RestTemplate根据获取到的IP和端口号访问服务即可。具体实现代码如下:web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importorg.springframework.web.client.RestTemplate;importjavax.annotation.Resource;@RestControllerpublicclassConsumerController{//Ribbon@Resource提供的负载均衡对象privateLoadBalancerClientloadBalancerClient;//Spring提供了一个Restful请求对象@ResourceprivateRestTemplaterestTemplate;@GetMapping("/consumer")publicStringconsumer(@RequestParamStringname){//根据Ribbon+Nacos提供的对象获取服务实例ServiceInstanceserviceInstance=loadBalancerClient.choose("springcloud-nacos-provider");//获取服务实例中的ipStringip=serviceInstance.getHost();//获取服务实例中的端口号intport=serviceInstance.getPort();//使用restTemplate请求并获取结果Stringresult=restTemplate.getForObject("http://"+ip+":"+port+"/call/"+name,String.class);返回结果;}}上面程序的执行结果如下图所示:2.注解方法调用使用注解方法调用服务要简单的多。创建服务提供者的方法同上,这里不再赘述。现在,让我们创建一个注解服务调用者Consumer第一步:创建一个SpringBoot项目,添加spring-web和nacos-discovery依赖。具体依赖如下:org.springframework.bootspring-boot-starter-webcom.alibaba.cloudspring-cloud-starter-alibaba-nacos-discovery第二步:设置Nacos相关配置,在里面添加如下配置application.yml:spring:application:name:springcloud-nacos-consumer#项目名(nacos注册的服务名)cloud:nacos:discovery:username:nacos#nacos登录用户名password:nacos666#nacos密码server-addr:127.0.0.1:8848#nacos服务器地址server:port:8092#项目启动端口号第三步:在项目启动类中,使用SpringJavaConfig声明RestTemplate对象。这一步需要给RestTemplate对象添加@LoadBalanced注解。添加该注解后,RestTemplate对象可以自动支持负载均衡,如下代码所示:importorg.springframework.boot.SpringApplication;导入org.springframework.boot.autoconfigure.SpringBootApplication;导入org.springframework.cloud.client.loadbalancer.LoadBalanced;导入org.springframework.context.annotation.Bean;导入org.springframework.web.client.RestTemplate;@SpringBootApplicationpublic类RibbonAnnotationConsumerApplication{publicstatic(String[]args){SpringApplication.run(RibbonAnnotationConsumerApplication.class,args);}@LoadBalanced//让RestTemplate自动支持Ribbon负载均衡@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}第四步:创建客户具体实现代码如下:.annotation.RestController;导入组织.springframework.web.client.RestTemplate;导入javax.annotation.Resource;@RestControllerpublicclassConsumerController{@ResourceprivateRestTemplaterestTemplate;@GetMapping("/消费者")PUblicStringconsumer(@RequestParamStringname){//请求并获取结果(springcloud-nacos-provider为Nacos服务id)Stringresult=restTemplate.getForObject("http://springcloud-nacos-provider/call/"+名称,字符串类);返回结果;}}上面程序的执行结果如下图所示:注解实现原理分析通过上面的代码,我们可以看出Nacos调用服务的关键是通过@LoadBalanced,为RestTemplateBalanced能力分配负载,才能正确调用服务,@LoadBalanced是如何实现的呢?要想知道这个问题的答案,就得去读一下LoadBalancerAutoConfiguration的源码LoadBalancerAutoConfiguration是一个实现客户端负载均衡器的自动装配类。它从春天开始。它有很多源代码内容。让我们来看看这里的一些核心方法:@BeanpublicSmartInitializingSingletonloadBalancedRestTemplateInitializerDeprecated(finalObjectProvider>restTemplateCustomizers){return()->{restTemplateCustomizers.ifAvailable((customizers)->{Iteratorvar2=this.restTemplates.iterator();while(var2.hasNext()){RestTemplaterestTemplate=(RestTemplate)var2.next();Iteratorvar4=customizers.iterator();while(var4.hasNext()){RestTemplateCustomizercustomizer=(RestTemplateCustomizer)var4.next();customizer.customize(restTemplate);}}});};}这里this.restTemplates.iterator()表示所有被@LoadBalanced注解修饰的RestTemplate对象,所有被@LoadBalanced注解修饰的RestTemplate对象都会转化为RestTemplateCustomizer对象,该对象的实现源码如下下:@Bean@ConditionalOnMissingBeanpublicRestTemplateCustomizerrestTemplateCustomizer(finalLoadBalancerInterceptorloadBalancerInterceptor){return(restTemplate)->{Listlist=newArrayList(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(列表);};}也就是说,所有被@LoadBalanced注解修饰的RestTemplate对象,都会为其添加一个loadBalancerInterceptor拦截器。拦截器的实现源码如下:publicclassLoadBalancerInterceptorimplementsClientHttpRequestInterceptor{privateLoadBalancerClientloadBalancer;私有LoadBalancerRequestFactoryLoadBalancer;私有LoadBalancerRequestFactoryLoad(LoadBalancerClientloadBalancer,LoadBalancerRequestFactoryrequestFactory){this.loadBalancer=loadBalancer;this.requestFactory=requestFactory;}publicLoadBalancerInterceptor(LoadBalancerClientloadBalancer){这个(loadBalancer,newLoadBalancerRequestFactory故事(负载均衡器));}publicClientHttpResponseintercept(finalHttpRequestrequest,finalbyte[]body,finalClientHttpRequestExecutionexecution)throwsIOException{URIoriginalUri=request.getURI();StringserviceName=originalUri.getHost(serviceName!=null,"请求URI不包含有效的主机名:"+originalUri);返回(ClientHttpResponse)this.loadBalancer.execute(serviceName,this.requestFactory.createRequest(request,body,execution));}}从上面的源码可以看出,@LoadBalanced的执行过程是@LoadBalanced注解修饰的RestTemplate对象会被LoadBalancerInterceptor拦截器拦截。拦截后会使用LoadBalancerClient对象根据负载均衡策略获取健康的服务实例,然后通过服务实例的IP和端口,调用实例方法完成服务请求。总结Nacos通过内置的Ribbon框架调用Restful服务。它有两种调用方式,通过代码或者通过注解。注解方式使用起来比较简单。只需要在RestTemplate对象上加上@LoadBalanced注解,就可以赋予请求对象负载均衡的能力。