客户端负载均衡Ribbon(一)

1.Ribbon

目前主流的负载方案分为两种:一种是集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如F5),也有软件的(比如Nginx)。另一种则是客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon就属于这种。

一句话:Ribbon是Netflix开源的一款用于客户端负载均衡的工具软件。

1.1 Ribbon模块

Ribbon模块如下:

·ribbon-loadbalancer:负载均衡模块,可独立使用,也可以和别的模块一起使用。Ribbon内置的负载均衡算法都实现在其中。

·ribbon-eureka:基于Eureka封装的模块,能够快速方便地集成Eureka。

·ribbon-transport:基于Netty实现多协议的支持,比如Http、Tcp、Udp等

·ribbon-httpclient:基于Apache HttpClient封装的REST客户端,继承了负载均衡模块,可以直接在项目中使用来调用接口。

·ribbon-example:Ribbon使用代码示例,通过这些示例能够让你的学习事半功倍。

·ribbon-core:一些比较核心且具有通用性的代码,客户端API的一些配置和其他API的定义。

1.2 Ribbon使用

Ribbon依赖:

<dependency>
    <groupId>com.netflix.ribbon</groupId>
    <artifactId>ribbon</artifactId>
    <version>2.7.17</version>
</dependency>

接下来编写一个客户端来调用接口:


2. RestTemplate结合Ribbon使用

上面是简单地使用Ribbon进行了负载的一个调用,意味着Ribbon是可以单独使用的。在Spring Cloud中使用Ribbon会更简单,因为Spring Cloud在Ribbon的基础上进行了一层封装,将很多配置都集成好了。本节将在Spring Cloud项目中使用Ribbon。

2.1 使用RestTemplate与整合Ribbon

Spring提供了一种简单便捷的模板类来进行API的调用,那就是RestTemplate。

2.1.1 使用RestTemplate

本节更仔细地讲解RestTemplate的具体使用方法。

首先我们看看GET请求的使用方式:在fsh-house服务的HouseController中增加两个接口,一个通过@RequestParam来传递参数,返回一个对象信息;另一个通过@PathVariable来传递参数,返回一个字符串。代码如下:

@GetMapping("/data")
    public HouseInfo getData(@RequestParam("name")String name){
        return new HouseInfo(1L,"上海","虹口","东体小区");
    }
    
    @GetMapping("/data/{name}")
    public String getData2(@PathVariable("name")String name) {
        return name;
    }
}

在fsh-substitution服务中用RestTemplate来调用我们刚刚定义的两个接口,如代码:

@GetMapping("/data")
    public HouseInfo getData(@RequestParam("name")String name){
        return restTemplate.getForObject("http://localhost:8081/house/data?name="+name, HouseInfo.class);
    }
    
    @GetMapping("/data/{name}")
    public String getData2(@PathVariable("name")String name){
        return restTemplate.getForObject("http://localhost:8081/house/data/{name}", String.class,name);
    }

注意getForObject方法的几个参数:url,返回值类型,参数。


除了getForObject,还可以使用getForEntity来获取数据,代码如下:

@GetMapping("/data")
    public HouseInfo getData3(@RequestParam("name")String name){
        ResponseEntity<HouseInfo> responseEntity=restTemplate.getForEntity("http://localhost:8081/house/data?name="+name, HouseInfo.class);
        if(responseEntity.getStatusCodeValue()==200) {
            return responseEntity.getBody();
        }
        return null;
    }

getForEntity中可以获取返回的状态吗、请求头等信息,通过getBody获取响应的内容。


接下来看看怎么使用POST方式调用接口。在HouseController中增加一个save方法用来接收HouseInfo数据,如代码所示:

@PostMapping("/save")
    public Long addData(@RequestBody HouseInfo houseInfo) {
        System.out.println(houseInfo.getName());
        return 1001L;
    }

接着写调用代码,用postForObject来调用,如代码所示:

@GetMapping("/save")
    public Long add() {
        HouseInfo houseInfo=new HouseInfo();
        houseInfo.setCity("上海");
        houseInfo.setRegion("虹口");
        houseInfo.setName("兴旺新区");
        Long id=restTemplate.postForObject("http://localhost:8081/house/save", houseInfo, Long.class);
        
        return id;
    }
}

除了postForObject还可以使用postForEntity方法,用法都一样。

除了get和post对应的方法之外,RestT还提供了put、delete等操作方法,还有一个比较实用的就是exchange方法。exchange可以执行get、post、put、delete这4种请求方式。可自行学习。

2.1.2 整合Ribbon

在Spring Cloud项目中集成Ribbon只需要加入下面的以来即可,其实也可以不用额皮质,因为Eureka中已经引用了Ribbon,如代码所示:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>

这个配置我们加在fangjia-fsh-substitution-service中。

2.2 RestTemplate负载均衡示例

对之前的代码进行改造,输出一些内容,证明我们集成的Ribbon是有效的。

改造hello接口,在接口中输出当前服务的端口,用来区分调用的服务,代码如下:

@RestController
@RequestMapping("/house")
public class HouseController{
    @Value("${server.port}")
    private String severPort;

    @GetMapping("/hello")
    public String hello(){
        return "Hello"+severPort;
    }
}

接着改造callHello接口的代码,将调用结果输出到控制台,如代码所示:

@RestController
@RequestMapping("/substitution")
public class SubstitutionController{
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/callHello")
    public String callHello(){
        String result=restTemplate.getForObject("http://fsh-house/house/hello",String.class);
        System.out.println("调用结果:"+result);
        return result;
    }

}
重启服务,访问对应端口,查看控制台输出,就知道负载有没有起作用了。


2.3 @LoadBalanced注解原理

为什么在RestTemplate上加了一个@LoadBalanced之后,RestTemplate就能够跟Eureka结合了,可以使用服务名称去调用接口,还可以负载均衡?

这功劳应归功于Spring Cloud给我们做了大量的底层工作,因为它将这些都封装好了。

主要的逻辑就是给RestTemplate增肌拦截器,在请求之前对请求的地址进行替换,或者根据具体的负载策略选择服务地址,然后再去调用,这就是@LoadBalanced的原理。

深入原理,以后在研究吧。


2.4 Ribbon API使用

当你有一些特殊的需求,想通过Ribbon获取对应的服务信息,可以使用LoadBalancerClient来获取,比如你想获取一个fsh-house服务的服务地址,可以通过LoadBalancerClient的choose方法选择一个:

@Autowired
private LoadBalancerClient loadBalancer;

@GetMapping("/choose")
public Object chooseUrl(){
    ServiceInstance instance=loadBalancer.choose("fsh-house");
    return instance;
}

2.5 Ribbon饥饿加载

在进行服务调用的时候,如果网络情况不好,第一次调用会超时。通过配置eager-load来提前初始化客户端就可以解决这个问题:

#开启Ribbon的饥饿加载模式
ribbon.eager-load.enabled=true
#指定需要饥饿加载的服务名,也就是你需要调用的服务
ribbon.eager-load.clients=fsh-house


3.负载均衡策略介绍

Ribbon作为一款客户端负载均衡框架,默认的负载策略是轮询,同时也提供了很多其他的策略能够让用户根据自身的业务需求进行选择。


4.自定义负载策略

通过实现IRule接口可以自定义负载策略,主要的选择服务逻辑在choose方法中。


5.配置详解

5.1 常用配置

5.1.1 禁用Eureka

当我们在RestTemplate上添加@LoadBalanced注解后,就可以用服务名称来调用接口了,当有多个服务的时候,还能做负载均衡。这是因为Eureka中的服务信息已经被拉取到了客户端本地,如果我们不想和Eureka集成,可通过一下配置将其禁用:

#禁用Eureka
ribbon.eureka.enabled=false

当我们禁用了Eureka之后,就不能使用服务名称去调用接口了,必须指定服务地址。

5.1.2 配置接口地址列表

禁用Eureka之后就需要手动配置调用的服务地址了,配置如下:

#禁用Eureka后手动配置服务地址
fsh-house.ribbon.listOServers=localhost:8081,localhost:8083

这个配置是针对具体服务的,前缀就是服务名称,配置完之后就可以和之前一样使用服务名称来调用接口了。

5.1.3 配置负载均衡策略

Ribbon默认的策略是轮询。可以通过配置指定服务使用哪种策略来进行负载操作:

#配置负载均衡策略
fsh-house.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

5.1.4 超时时间

Ribbon中有两种时间相关的设置,分别是请求连接的超时时间和请求处理的超时时间,设置规则如下:

#请求连接的超时时间
ribbon.connectTimeOut=2000
#请求处理的超时时间
ribbon.readTimeOut=5000

5.1.5 请求重试

在服务调用过程中,难免会发生一些异常情况,比如网络问题导致的调用失败,当调用失败时通过重试机制进行再次调用,可以减少出错的数量,同时也需要注意多次调用导致的脏数据问题。通过下面的配置可以对Ribbon的重试次数进行配置:

#对当前实例的重试次数
ribbon.maxAutoRetries=1
#切换实例的重试次数
ribbon.maxAutoRetriesNextServer=3
#对所有操作请求都进行重试
ribbon.okToRetryOnAllOperations=true


6 重试机制

重试机制就是当Ribbon发现请求的服务不可到达时,重新请求另外的服务。

6.1 RetryRule重试

最简单的方法就是利用Ribbon自带的重试策略进行重试,此时只需要指定某个服务的负载策略为重试策略即可:

fsh-house.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RetryRule

6.2 Spring Retry重试

除了使用Ribbon自带的重试策略,我们还可以通过基础Spring Retry来进行重试操作

在pom中添加Spring Retry的依赖,如代码所示:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

配置重试次数等信息:

#对当前实例的重试次数
ribbon.maxAutoRetries=1
#切换实例的重试次数
ribbon.maxAutoRetriesNextServer=3
#对所有操作请求都进行重试
ribbon.okToRetryOnAllOperations=true

猜你喜欢

转载自www.cnblogs.com/xc-xinxue/p/12452467.html
今日推荐