spring cloud之服务调用及使用ribbon实现负载均衡(三)

来源:互联网 发布:java能开发单片机吗 编辑:程序博客网 时间:2024/06/06 13:57

Spring Cloud R巾bon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix rbbon 实现。 通过 Spring Cloud 的封装, 可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用。 Spring Cloud rbbon 虽然只是一个工具类框架,它不像服务注册中心、 配置中心、 API 网关那样需要独立部署, 但是它几乎存在于每一个Spring Cloud 构建的微服务和基础设施中。 因为微服务间的调用,API 网关的请求转发等内容实际上都是通过伈bbon 来实现的,包括后续我们将要介绍的 Feign, 它也是基于 Ribbon实现的工具。

在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。


我们通常所说的负载均衡都指的是服务端负载均衡, 其中分为硬件负载均衡和软件负载均衡。 硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则
是通过在服务器上安装
些具有均衡负载功能或模块的软件来完成请求分发工作, 比如Nginx等。 不论采用硬件负载均衡还是软件负载均衡,硬件负载均衡的设备或是软件负载均衡的软件模块都会维护个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。
当客户端发送请求到负载均衡设备的时候, 该设备按某种算法(比如线性轮询、 按权重负载、 按流量负载等)从维护的可用服务端清单中取出
台服务端的地址, 然后进行转发。而客户端负载均衡和服务端负载均衡最大的不同点在千上面所提到的服务清单所存储的位置。 在客户端负载均衡中, 所有客户端节点都维护着自己要访问的服务端清单, 而这些服务端的清单来自于服务注册中心,比如上章我们介绍的Eureka服务端。同服务端负载均衡的架构类似, 在客户端负载均衡中也需要心跳去维护服务端清单的健康性, 只是这个步骤需要与服务注册中心配合完成。


所有微服务汇集到了 Eureka a之中,而客户端的调用也应该通过 之中,而客户端的调用也应该通过 Eureka完成。而这种调用 就可以利完成。而这种调用 就可以利Ribbon Ribbon技术来实现。
Ribbon是一个服务调用 的组件,并且客户端实现负载均衡处理。器可以使是一个服务调用 的组件,并且客户端实现负载均衡处理。器可以使是一个服务调用 的组件,并且客户端实现负载均衡处理。器可以使Nginx 、 HAProxy 、LVS

1.服务提供者

被调用的服务controller

package com.alen.controller;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;/** * @create 2017-11-27 17:32 **/@RestControllerpublic class HelloController {    @RequestMapping("/hello")    public String home(@RequestParam Integer age) {        age=age+10;        return age.toString();    }}启动类 

package com.alen;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;@SpringBootApplication//通过注解@EnableEurekaClient 表明自己是一个eurekaclient.@EnableEurekaClientpublic class EurekaClientApplication {   public static void main(String[] args) {      SpringApplication.run(EurekaClientApplication.class, args);   }}pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud</artifactId>        <groupId>com.alen</groupId>        <version>0.0.1-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <packaging>jar</packaging>    <artifactId>eureka-client</artifactId>    <dependencies>        <!--eureka client -->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>application.properties文件有个坑就是配了server.context-path=
消费者就调不通,不知道什么鬼

#端口server.port=8087eureka.instance.hostname=localhost#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/#这在以后的服务与服务之间相互调用一般都是根据这个namespring.application.name=eureka-client#eureka.client.register-with-eureka=true#eureka.client.fetch-registry=true# 心跳时间,即服务续约间隔时间(缺省为30s)eureka.instance.lease-renewal-interval-in-seconds= 5# 发呆时间,即服务续约到期时间(缺省为90s)eureka.instance.lease-expiration-duration-in-seconds=15     


2.服务调用者


2.1启动类

package com.alen;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.netflix.feign.EnableFeignClients;import org.springframework.context.annotation.Bean;import org.springframework.web.client.RestTemplate;@SpringBootApplication//通过注解@EnableEurekaClient 表明自己是一个eurekaclient.@EnableEurekaClientpublic class RibbonServiceApplication {   public static void main(String[] args) {      SpringApplication.run(RibbonServiceApplication.class, args);   }   /**    * 向程序的ioc注入一个bean: restTemplate;    * 并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。    */   @Bean   @LoadBalanced    public     RestTemplate restTemplate() {      return new RestTemplate();   }}


2.2消费者service

package com.alen.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;/** * 测试调用 * * @create 2017-11-27 17:14 **/@Servicepublic class HelloService {    @Autowired    private LoadBalancerClient loadBalancerClient;    @Autowired    RestTemplate restTemplate;    @Value("${consumerURL}")    private String consumerUrl;    public String getAge(Integer age) {        //打印一下到底调用的是哪个        ServiceInstance serviceInstance = this.loadBalancerClient.choose("eureka-client");        System.out.println("===" + ":" + serviceInstance.getServiceId() + ":" + serviceInstance.getHost() + ":" + serviceInstance.getPort());        System.out.print(consumerUrl);
         return restTemplate.getForObject(consumerUrl + age, String.class);
}}

2.3getForEntity

getForEntity(String url, Class responseType, Object... urlVariables):

该方法提供 了三个参数, 其中 url为请求的地址,responseType为请求响应体,body的包装类型,urlVariablesurl中的参数绑定。GET请求的参数绑定通常使用url中拼接的方式, 比如http://USER-SERVICE/user?name=did我们可以像这样自已将参数拼接到url中,但更好的方法是在url中使用占位符并

配合 urlVariables参 数实现GET请 求的 参数 绑 定, 比 如url定 义 为http://USER-SERVICE/user?name={l),然后可以这样来调用:getForEntity("http://USER-SERVICE/user?name={l}", String.class, "ddi")'中第三个参数didi会替换url中的{ 1} 占位符。 这里需要注意的是, 由千urlVariables参数是个数组, 所以它的顺序会对应url中 占位符定义的数字顺序。

• getForEntity(String url, Class responseType, Map urlVariables):

该方法提供的参数中, 只有 urlVariables 的参数类型与上面的方法不同。 这里使用了Map类型, 所以使用该方法进行参数绑定时需要在占位符中指定Map中参数的key值, 比如url定义为http://USER-SERVICE/user?name={name),Map类型urlVariables中, 我们就需要putkeyname的参数来绑定url{name}占位符的值, 比如:

RestTemplate restTemplate = new RestTemplate();

Map<String, String> params = new HashMap<>();

params.put("name", "dada");

ResponseEntity<String> responseEntity = resTemplate.getForEntity("htp://USERSERVICE/user?name={name}", String.class, params);

• getForEntity(UR工 url, Class responseType):

该方法使用URI对象来替代之前的urlurlVariables参数来指定访问地址和参数绑定。URIJDK java.net包下的个类,它表示个统资源标识符(Uniform Resource Identifer)引用。 比如下面的例子:

RestTemplate restTemplate = new RestTemplate();

UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://USER-SERVICE/user?name={name}").build().expand("dodo").encode();

URI uri = uriComponents.toUri();

ResponseEntiy<String> responseEntity = resTemplate.getForEntity(uri,

String.class).getBody();


2.4 getForObject

第二种: getForObject函数。该方法可以理解为对ge七ForEntity的进步封装,它通过HttpMessageConverterEx七ractorHTTP的请求响应体body内容进行对象转换, 实现请求直接返回包装好的对象内容。

比如:RestTemplate restTemplate = new RestTemplate();

String result =restTemplate.getForObject(uri, String.class);

bodyUser对象时, 可以直接这样实现:

RestTemplate restTemplate=new RestTemplate();

User result = restTemplate.getForObject(uri, User.class);

当不需要关注请求响应除 body 外的其他内容时, 该函数就非常好用, 可以少个从Response中获取body的步骤。 它与getForEnEtity函数类似, 也提供了三种不同的重载实现。


• getForObject (String url, Class responseType, Object... urlVariables):

getForEntity的方法类似,url参数指定访问的地址,responseType参数

定义该方法的返回类型, urlVariables 参数为url中占位符对应的参数。


• getForObject(String url, Class responseType, Map urlVariables):

在该函数中,使用 Map 类型的urlVariables替代上面数组形式的urlVariables,

因此使用时在 url中需要将占位符的名称与Map类型中的key-对应设置。


• getForObject(UR工 url, Class responseType):

该方法使用URI对象来

替代之前的 url和urlVariables参数使用。


2.5 POST请求

RestTemplate中, 对POST 请求时可以通过如下三个方法进行调用实现。

种:postForEntity函数。

该方法同 GET请求中的ge七ForEntity类似, 会在调用后返回ResponseEntity<T>对象, 其中T为请求响应的body类型。 比如下面这个例子, 使用postForEntity提交POST请求到USER-SERVICE服务的/user接口,提交的body内容为user对象, 请求响应返回的body类型为Sting

RestTemplate restTemplate = new RestTemplate();

User user = new User("didi", 30);

ResponseEntity<String> responseEntity=

restTemplate.postForEnity("htp://USER-SERVICE/user", user, String.class);

String body = responseEntity.getBody();


postForEnEtity 函数也实现了三种不同的重载方法。

• postForEntity(String url, Objec七 request, Class responseType,Object... uriVariables)

• postForEntity(String url, Object request, Class responseType,Map uriVariables)

• postForEntity(URI url, Object reques七, Class responseType)

这些函数中的参数用法大部分与 getForEntity致, 比如, 第个重载函数和第二个重 载 函 数中的 uriVariables 参 数都用 来对 url 中的参 数进行绑定使用;

responseType参数是对请求响应的body内容的类型定义。 这里需要注意的是新增加的request参数, 该参数可以是个普通对象, 也可以是个HttpEntity对象。 如果是个普通对象, 而非HttpEntity对象的时候, RestTempla七e会将请求对象转换为

个HttpEnEtity对象来处理, 其中Object就是 request的类型, request内容会被视作完整的body来处理;而如果 request是个HttpEnEtity对象, 那么就会被当作个完成的HTTP请求对象来处理, 这个 request中不仅包含了body的内容, 也包含了header的内容。


第二种: postForObject 函数。 该方法也跟getForObject的类型类似, 它的作用是简化postForEntity的后续处理。 通过直接将请求响应的body内容包装成对象来返回使用, 比如下面的例子:

RestTemplate restTemplate=new RestTemplate();

User user = new User("didi", 20);

String postResult = restTemplate.postForObject("htp: //USER-SERVICE/user", user,String.class);

postForObject函数也实现了三种 不同的重载方法:

• postForObject(String url, Object request, Class responseType,Object... uriVariables)

• postForObject(String url, Object request, Class responseType,Map uriVariables)

• postForObject(URI url, Object reques七, Class responseType)

这三个函数除了返回的对象类型不同, 函数的传入参数均与postForEntity致,

因此可参考之前postForEnEtity的说明。

第三种: postForLocation函数。 该方法实现了以POST请求提交资源, 并返回新

资源的URI, 比如下面的例子:

User user = new User("didi", 40);

URI responseURI = restTemplate.postForLocation("http://USER-SERVICE/user", user);

postForLocation函数也实现了三种不同的重载方法:

• postForLocation(Stringurl, Object request, Object...urlVariables)

• postForLocation(String url, Object request, Map urlVariables)

• postForLocation(URI url, Object request)

由于postForLocation 函数会返回新资源的URI, 该UR就相当于指定了返回类型,所以此方法实现的POST请求不需要像postForEntity和postForObject那样指定responseType。 其他的参数用法 相同。


2.6 消费者controller

package com.alen.controller;import com.alen.service.HelloService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.context.annotation.Bean;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.client.RestTemplate;/** * @create 2017-11-27 17:32 **/@RestControllerpublic class HelloController {    @Autowired    private HelloService helloService;    /**     * 服务调用 消费者     * @param age     * @return     */    @RequestMapping("/getage")    public String getConsumer(@RequestParam Integer age) {        return helloService.getAge(age);    }}


配置文件application.properties

#端口server.port=8082eureka.instance.hostname=localhost#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/#这在以后的服务与服务之间相互调用一般都是根据这个namespring.application.name=ribbon-service#eureka.client.register-with-eureka=true#eureka.client.fetch-registry=true# 心跳时间,即服务续约间隔时间(缺省为30s)eureka.instance.lease-renewal-interval-in-seconds= 5# 发呆时间,即服务续约到期时间(缺省为90s)eureka.instance.lease-expiration-duration-in-seconds=15     consumerURL=http://EUREKA-CLIENT/hello?age=

pom.xml

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <parent>        <artifactId>spring-cloud</artifactId>        <groupId>com.alen</groupId>        <version>0.0.1-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>    <packaging>jar</packaging>    <artifactId>ribbon-service</artifactId>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-eureka</artifactId>        </dependency>        <!--spring-cloud-starter-eureka已经引了,所以可引可不引-->        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-ribbon</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>

3.ribbon

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组 件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现 自定义的负载均衡算法。

Ribbon提供的主要负载均衡策略介绍

简单轮询负载均衡(RoundRobin)

以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。

随机负载均衡 (Random)

随机选择状态为UP的Server

加权响应时间负载均衡 (WeightedResponseTime)

区域感知轮询负载均衡(ZoneAware)

区域感知负载均衡内置电路跳闸逻辑,可被配置基于区域同源关系(Zone Affinity,也就是更倾向于选择发出调用的服务所在的托管区域内,这样可以降低延迟,节省成本)选择目标服务实例。它监控每个区域中运行实例的行 为,而且能够实时的快速丢弃一整个区域。这样在面对整个区域故障时,帮我们提升了弹性。

Ribbon自带负载均衡策略比较

策略名策略声明策略描述实现说明BestAvailableRulepublic class BestAvailableRule extends ClientConfigEnabledRoundRobinRule选择一个最小的并发请求的server逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的serverAvailabilityFilteringRulepublic class AvailabilityFilteringRule extends PredicateBasedRule过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态WeightedResponseTimeRulepublic class WeightedResponseTimeRule extends RoundRobinRule根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。一 个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择 server。RetryRulepublic class RetryRule extends AbstractLoadBalancerRule对选定的负载均衡策略机上重试机制。在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的serverRoundRobinRulepublic class RoundRobinRule extends AbstractLoadBalancerRuleroundRobin方式轮询选择server该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。
RandomRulepublic class RandomRule extends AbstractLoadBalancerRule随机选择一个server该策略实现了从服务实例清单中随机选择个服务实例的功能。
ZoneAvoidanceRulepublic class ZoneAvoidanceRule extends PredicateBasedRule复合判断server所在区域的性能和server的可用性选择server使 用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个 zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的 Server。

配置properties file (sample-client.properties)

# Max number of retries   
ribbon.MaxAutoRetries=1    
# Max number of next servers to retry (excluding the first server) 
ribbon.MaxAutoRetriesNextServer=1 
 # Whether all operations can be retried for this client  
ribbon.OkToRetryOnAllOperations=true  
# Interval to refresh the server list from the source  
ribbon.ServerListRefreshInterval=2000   
# Connect timeout used by Apache HttpClient  
ribbon.ConnectTimeout=3000   
# Read timeout used by Apache HttpClient  
ribbon.ReadTimeout=3000  
# Initial list of servers, can be changed via Archaius dynamic property at runtime  
ribbon.listOfServers=testserver1:80,testserver2 :80,testserver3:80   
ribbon.EnablePrimeConnections=true 



4.自定义Ribbon客户端

自定义Ribbon客户端有两种方式:通过配置类或配置文件

您可以使用<client>.ribbon.*中的外部属性来配置Ribbon客户端的某些位,这与使用Netflix API本身没有什么不同,只能使用Spring Boot配置文件。本机选项可以在CommonClientConfigKey(功能区内核心部分)中作为静态字段进行检查。

Spring Cloud还允许您通过使用@RibbonClient声明其他配置(位于RibbonClientConfiguration之上)来完全控制客户端。例:

@Configuration@RibbonClient(name = "foo", configuration = FooConfiguration.class)public class TestConfiguration {}

在这种情况下,客户端由RibbonClientConfiguration中已经存在的组件与FooConfiguration中的任何组件组成(后者通常会覆盖前者)。

警告
FooConfiguration必须是@Configuration,但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。

4.1通过配置类来实现ribbion自动化配置

由于Ribbon中定义的每 一个接口都有多种不同的策略实现,同时这些接口之间又有 一定的 依赖关系,这使得第 一次使用rbbion的开发者很难上手,不知道如何选择具体的实现策略以及如何组织它们 的关系。 Spring Cloud凡bbon中的自动化配置恰恰 能够解决这样的痛点,在引入Spring CloudRibbon的 依赖之后, 就能够自动化构建下面这些接口的实现。

• IClientConfig: Ribbon 的 客户端配置 , 默认采用 com.netflix.client. config.DefaultClientConfigimpl实现.

• IRule: Ribbon 的负载均衡策略 , 默认采用 com.netflix.loadbalancer.

  ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问。

• IPing: rbbon的实例检查策略,默认采用com.netf巨x.loadbalancer.NoOp贮ng

实现, 该 检查策略是一个特殊的实现,实际上它并不会检查实例是否可用, 而是始终返回true, 默认认为所有服务实例都是可用的 。

• ServerList<Server>: 服务实例清单的维护机制, 默认采用 com.netflix.loadbalancer.ConfigurationBasedServerList实现。

• ServerListFilter<Server>: 服 务 实 例 清 单过滤机 制 , 默认采用 org.

springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter实现, 该策略能够优先过滤出与请求调用 方处于同区域的服务实例。

• ILoadBalancer: 负载均衡器, 默 认采用 com.netflix.loadbalancer.ZoneAwareLoadBalancer实现, 它具备了区域感知的 能力。

上面这些自动化配置内容仅在没有引入Spring CloudEureka等服务治理框架时如此,

在同时引入Eureka和rbbion依赖时,自动化配置会有 一些不同


1.为了指定指定服务采用新定的规则,需要在启动类同级目录下添加类“ExcludeComponentScan.java”:

public @interface ExcludeFromComponentScan {}

2..创建一个类型的bean并将其放置在@RibbonClient配置允许您覆盖所描述的每个bean。例:

@Configuration@ExcludeFromComponentScan//使用@RibbonClient注解来实现更细粒度的客户端配置, 比如下// 面的代码实现了为ribbon-service-provider服务使用MyLoadBalanceConfig中的配 置。@RibbonClient(name = "ribbon-service-provider", configuration = MyLoadBalanceConfig.class)public class MyLoadBalanceConfig {    @Bean    public IRule ribbonRule() { // 其中IRule就是所有规则的标准        return new com.netflix.loadbalancer.RandomRule(); // 随机的访问策略    }}

3.启动类

@SpringBootApplication//通过注解@EnableEurekaClient 表明自己是一个eurekaclient.@EnableEurekaClient//@Configuration这个注解是不能@SpringBootApplication的所在扫描到的,//否则将自定义的配置将失效。所以需要将MyLoadBalanceConfig.java排除在包扫描之外,//用自定义的注解@ExcludeFromComponentScan,//然后在启动类加注解//@ComponentScan(excludeFilters{@ComponentScan.Filter(type=FilterType.ANNOTATION,//    value=ExcludeFromComponentScan.class)})将其排除。@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {ExcludeFromComponentScan.class})})public class RibbonServiceApplication {   public static void main(String[] args) {      SpringApplication.run(RibbonServiceApplication.class, args);   }   /**    * 向程序的ioc注入一个bean: restTemplate;    * 并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。    */   @Bean   @LoadBalanced    public     RestTemplate restTemplate() {      return new RestTemplate();   }}

MyLoadBalanceConfig 必须是@Configuration,但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重叠的包中,或者指定要在@ComponentScan)。

参考:http://blog.csdn.net/shunhua19881987/article/details/75466797

4.2使用属性自定义Ribbon客户端

从版本1.2.0开始,Spring Cloud Netflix现在支持使用属性与Ribbon文档兼容来自定义Ribbon客户端。

这允许您在不同环境中更改启动时的行为。

支持的属性如下所示,应以<clientName>.ribbon.为前缀:

  • NFLoadBalancerClassName:应实施ILoadBalancer

  • NFLoadBalancerRuleClassName:应实施IRule

  • NFLoadBalancerPingClassName:应实施IPing

  • NIWSServerListClassName:应实施ServerList

  • NIWSServerListFilterClassName应实施ServerListFilter

注意
在这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的bean和由Spring Cloud Netflix提供的默认值。

要设置服务名称usersIRule,您可以设置以下内容:

application.yml
users:  ribbon:    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule


测试代码:

package com.alen.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;import org.springframework.stereotype.Service;import org.springframework.web.client.RestTemplate;/** * 测试调用 * * @create 2017-11-27 17:14 **/@Servicepublic class HelloService {    @Autowired    private LoadBalancerClient loadBalancerClient;    @Autowired    RestTemplate restTemplate;    @Value("${eurekaclientURL}")    private String eurekaclientURL;    @Value("${ribbonserviceproviderURL}")    private String ribbonserviceproviderURL;    public String getAge(Integer age) {        //打印一下到底调用的是哪个        ServiceInstance serviceInstance = this.loadBalancerClient.choose("eureka-client");        System.out.println("===" + ":" + serviceInstance.getServiceId() + ":" + serviceInstance.getHost() + ":" + serviceInstance.getPort());        ServiceInstance serviceInstance2 = this.loadBalancerClient.choose("ribbon-service-provider");        System.out.println("===" + ":" + serviceInstance2.getServiceId() + ":" + serviceInstance2.getHost() + ":" + serviceInstance2.getPort());        restTemplate.getForObject(ribbonserviceproviderURL + age, String.class);        return restTemplate.getForObject(eurekaclientURL + age, String.class);    }}

#端口server.port=8083eureka.instance.hostname=localhost#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/#这在以后的服务与服务之间相互调用一般都是根据这个namespring.application.name=ribbon-service#eureka.client.register-with-eureka=true#eureka.client.fetch-registry=true# 心跳时间,即服务续约间隔时间(缺省为30s)eureka.instance.lease-renewal-interval-in-seconds= 5# 发呆时间,即服务续约到期时间(缺省为90s)eureka.instance.lease-expiration-duration-in-seconds=15     eurekaclientURL=http://EUREKA-CLIENT/hello?age=ribbonserviceproviderURL=http://ribbon-service-provider/hello?age=#consumerURL=http://localhost:8087/hello?age=#设置ribbon-service-provider服务为随机轮循ribbon-service-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule#设置eureka-client服务轮询策略  ,轮询index,选择index对应位置的servereureka-client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRule



所有的服务名字一定要保持致,如果不则会认为是两个无法进行负载均衡。可以启动消费者去调用就能实现负载均衡。

结果

===:eureka-client:localhost:8073
===:ribbon-service-provider:localhost:8065
===:eureka-client:localhost:8073
===:ribbon-service-provider:localhost:8065
===:eureka-client:localhost:8082
===:ribbon-service-provider:localhost:8084
===:eureka-client:localhost:8072
===:ribbon-service-provider:localhost:8084


参考:https://springcloud.cc/spring-cloud-dalston.html#ribbon-child-context-eager-load

https://yq.aliyun.com/articles/61823

阅读全文
0 0
原创粉丝点击