您的位置:首页 > 编程语言 > Java开发

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

2017-11-28 10:36 1731 查看
Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix rbbon 实现。 通过 Spring Cloud 的封装, 可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用。 Spring Cloud rbbon 虽然只是一个工具类框架,它不像服务注册中心、 配置中心、 API 网关那样需要独立部署, 但是它几乎存在于每一个Spring Cloud 构建的微服务和基础设施中。 因为微服务间的调用,API 网关的请求转发等内容实际上都是通过ribbon 来实现的,包括后续我们将要介绍的 Feign, 它也是基于 Ribbon实现的工具。
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。

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

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

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
**/
@RestController
public 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.
@EnableEurekaClient
public 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=8087
eureka.instance.hostname=localhost
#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。
eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/
#这在以后的服务与服务之间相互调用一般都是根据这个name
spring.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.
@EnableEurekaClient
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();
}
}


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
**/
@Service
public 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.3 getForEntity

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

该方法提供 了三个参数, 其中 url为请求的地址,responseType为请求响应体,body的包装类型,urlVariables为url中的参数绑定。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中, 我们就需要put一个key为name的参数来绑定url中{name}占位符的值, 比如:
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<>();
params.put("name", "dada");
ResponseEntity<String> responseEntity = res七Template.getForEntity("h七tp://USERSERVICE/user?name={name}", String.class, params);
• getForEntity(UR工 url, Class responseType):
该方法使用URI对象来替代之前的url和urlVariables参数来指定访问地址和参数绑定。URI是JDK 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();
ResponseEnti七y<String> responseEntity = res七Template.getForEntity(uri,
String.class).getBody();

2.4 getForObject

第二种: getForObject函数。该方法可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换, 实现请求直接返回包装好的对象内容。
比如:RestTemplate restTemplate = new RestTemplate();

String result =restTemplate.getForObject(uri, String.class);
当body是一个User对象时, 可以直接这样实现:
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.postForEn七ity("http://USER-SERVICE/user", user, String.class);
String body = responseEntity.getBody();

postForEnEtity 函数也实现了三种不同的重载方法。
• postForEntity(String url, Object request, Class responseType,Object... uriVariables)
• postForEntity(String url, Object request, Class responseType,Map uriVariables)
• postForEntity(URI url, Object request, Class responseType)
这些函数中的参数用法大部分与 getForEntity一致, 比如, 第一个重载函数和第二个重 载 函 数中的 uriVariables 参 数都用 来对 url 中的参 数进行绑定使用;
responseType参数是对请求响应的body内容的类型定义。 这里需要注意的是新增加的request参数, 该参数可以是一个普通对象, 也可以是一个HttpEntity对象。 如果是一个普通对象, 而非HttpEntity对象的时候, RestTemplate会将请求对象转换为一
个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("ht七p: //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
**/
@RestController
public 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=8082
eureka.instance.hostname=localhost
#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。
eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/
#这在以后的服务与服务之间相互调用一般都是根据这个name
spring.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自带负载均衡策略比较
策略名
策略声明
策略描述
实现说明
BestAvailableRule
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule
选择一个最小的并发请求的server
逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
AvailabilityFilteringRule
public class AvailabilityFilteringRule extends PredicateBasedRule
过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
WeightedResponseTimeRule
public class WeightedResponseTimeRule extends RoundRobinRule
根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。
一 个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择 server。
RetryRule
public class RetryRule extends AbstractLoadBalancerRule
对选定的负载均衡策略机上重试机制。
在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRule
public class RoundRobinRule extends AbstractLoadBalancerRule
roundRobin方式轮询选择server
该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。

RandomRule
public class RandomRule extends AbstractLoadBalancerRule
随机选择一个server
该策略实现了从服务实例清单中随机选择一个服务实例的功能。

ZoneAvoidanceRule
public 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提供的默认值。
要设置服务名称
users
IRule
,您可以设置以下内容:
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
**/
@Service
public 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=8083
eureka.instance.hostname=localhost
#设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。
eureka.client.serviceUrl.defaultZone=http\://localhost\:8081/eureka/
#这在以后的服务与服务之间相互调用一般都是根据这个name
spring.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对应位置的server
eureka-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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐