SpringCloud Netflix Ribbon

文章目录

一、 Ribbon简介

Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。ribbon默认提供很多种负载均衡算法,例如 轮询、随机 等等。甚至包含自定义的负载均衡算法。Ribbon可以用于解决并提供微服务的负载均衡的问题。

二、 使用Ribbon开发微服务

在Spring Cloud中,使用Ribbon技术开发Eureka Client组件还是非常方便的。我们在开发过程中,不需要像Dubbo那样关注服务的角色。无论是Provider还是Consumer都是一个微服务客户端,只是在编码层面上,服务消费者代码的开发相对比较麻烦。我们通过简单案例测试一下Spring Cloud中的微服务开发过程。
因为现在的Eureka Server部署在Linux中,并已为Linux定义了新的主机域名,需要先修改开发测试环境中的hosts文件。windows中的hosts文件位于:C:\windows\system32\dirvers\etc\hosts。新增内容如下:(IP根据具体情况配置)

192.168.14.128 eureka1
192.168.14.129 eureka2

1 创建springcloud工程 和 commons子模块

SpringCloud Netflix Ribbon

1.1 POM依赖

<?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">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>commons</module>
        <module>ribbonappservice</module>
        <module>ribbonappclient</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <groupId>com.bjsxt</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

1.2 commons 的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>springcloud</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>commons</artifactId>
</project>

1.3 commons子模块中user类 (包含set,get等方法)
SpringCloud Netflix Ribbon

2 开发服务提供者 - ribbonappservice

SpringCloud Netflix Ribbon

2.1 POM依赖

<?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>springcloud</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <!-- 服务提供方项目, 使用Ribbon技术开发 -->
    <artifactId>ribbonappservice</artifactId>
    <dependencies>
        <!-- spring cloud是通过http协议对外发布一个基于REST规则的微服务
             通过SpringMVC技术中的Controller对外提供微服务的。
         -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Eureka客户端依赖,spring cloud中,服务的提供者和消费者都是Eureka客户端。必须依赖此资源 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjsxt</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

2.2 服务提供者代码

package com.bjsxt.userservice.controller;

import com.bjsxt.entity.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

/**
 * 提供服务的控制器
 */
@Controller
public class UserController {
    @RequestMapping("/user/save")
    @ResponseBody
    public Map<String, Object> save(User user){
        System.out.println("新增用户数据: " + user);
        Map<String, Object> result = new HashMap<>();
        result.put("code", "200"); // 返回的状态码
        result.put("message", "新增用户成功"); // 返回的处理结果消息。
        return result;
    }
}

2.3 配置文件application.yml

server:
  port: 8082

spring:
  application:
    name: ribbon-app-service

eureka: # Eureka客户端,启动的时候,如果未配置Eureka服务端地址列表,则在localhost:8761注册
  client:
    service-url:  # 配置Eureka服务端地址,注册中心地址,多个地址使用逗号 ',' 分隔。
      defaultZone: http://localhost:8761/eureka/

2.4 启动类

package com.bjsxt.userservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 服务提供方启动类
 * 在Spring Cloud低版本中,如果开发的代码是Eureka Client(服务提供者和消费者),
 * 那么启动类上需要增加注解
 * @EnableEurekaClient - 当前应用是一个Eureka客户端
 * @EnableDiscoveryClient - 当前应用需要启动发现机制,就是找到Eureka服务端,并注册发现服务。
 */
@SpringBootApplication
public class RibbonAppServiceApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonAppServiceApp.class, args);
    }
}

2.5 检查Eureka Server中的服务注册情况(Eureka Server项目使用SpringCloud Netflix Eureka文章中的
SpringCloud Netflix Ribbon

3 开发服务消费者 - ribbonappclient

SpringCloud Netflix Ribbon

3.1 POM依赖

<?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>springcloud</artifactId>
        <groupId>com.bjsxt</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ribbonappclient</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjsxt</groupId>
            <artifactId>commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

3.2 控制器

package com.bjsxt.userclient.controller;

import com.bjsxt.entity.User;
import com.bjsxt.userclient.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Map;

/**
 * 服务消费端
 */
@Controller
public class UserController {
    @Autowired
    private UserService userService;

    @RequestMapping("/user/save")
    @ResponseBody
    public Map<String, Object> save(User user){
        // 调用本地服务代码,本地服务代码远程调用application service服务提供方。
        Map<String, Object> result = this.userService.save(user);
        System.out.println("远程调用返回的结果:" + result);

        return result;
    }

}

3.3 服务接口

package com.bjsxt.userclient.service;

import com.bjsxt.entity.User;

import java.util.Map;

public interface UserService {
    Map<String, Object> save(User user);
}

3.4 服务实现

package com.bjsxt.userclient.service.impl;

import com.bjsxt.entity.User;
import com.bjsxt.userclient.service.UserService;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

@Service
public class UserServiceImpl implements UserService {

    /**
     * 是Ribbon技术中的负载均衡客户端对象。其中封装了从Eureka Server上发现的所有的服务地址列表
     * 包括服务的名称,IP,端口等。
     */
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    /**
     * 远程方法调用。访问application service,访问的地址是:http://localhost:8080/user/save
     * @param user
     * @return
     */
    @Override
    public Map<String, Object> save(User user) {
        // 根据服务的名称,获取服务实例。服务名称就是配置文件yml中的spring.application.name
        // 服务实例包括,这个名称的所有服务地址和端口。
        ServiceInstance instance = this.loadBalancerClient.choose("ribbon-app-service");
        // 访问地址拼接
        StringBuilder builder = new StringBuilder("");
        builder.append("http://").append(instance.getHost())
                .append(":").append(instance.getPort()).append("/user/save")
                .append("?username=").append(user.getUsername())
                .append("&password=").append(user.getPassword())
                .append("&remark=").append(user.getRemark());
        System.out.println("本地访问地址:" + builder.toString());

        // 创建一个Rest访问客户端模板对象。
        RestTemplate template = new RestTemplate();

        // 约束响应结果类型
        ParameterizedTypeReference<Map<String, Object>> responseType =
                new ParameterizedTypeReference<Map<String, Object>>() {
                };

        // 远程访问application service。
        ResponseEntity<Map<String, Object>> response =
                template.exchange(builder.toString(), HttpMethod.GET,
                null, responseType);

        Map<String, Object> result = response.getBody();
        return result;
    }
}

3.5 配置文件application.yml

server:
  port: 8081

spring:
  application:
    name: ribbon-app-client

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

3.6 启动类

package com.bjsxt.userclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class RibbonAppClientApp {
    public static void main(String[] args) {
        SpringApplication.run(RibbonAppClientApp.class, args);
    }
}

三、 集中式与进程内负载均衡区别

业界主流的负载均衡解决方案有:集中式负载均衡和进程内负载均衡。

1 集中式负载均衡
即在客户端和服务端之间使用独立的负载均衡设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务端。 SpringCloud Netflix Ribbon
2 进程内负载均衡
将负载均衡逻辑集成到客户端组件中,客户端组件从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务端发起请求。Ribbon就是一个进程内的负载均衡实现。
SpringCloud Netflix Ribbon

四、 Ribbon常见的负载均衡策略

Ribbon就属于进程内负载均衡,它只是一个类库,集成于Eureka Client进程,Eureka Client进程通过访问注册中心Eureka Server发现服务列表,发现的服务列表信息是由ribbon来管理的。当访问Application Service的时候,Application Client会通过ribbon来找到合适的Application Service地址信息,并发起远程调用请求。

1 Ribbon中的常用负载均衡简介

1 轮询策略(默认) RoundRobinRule

轮询策略表示每次都顺序取下一个provider,比如一共有5个provider,第1次取第1个,第2次取第2个,第3次取第3个,以此类推

2 权重轮询策略(常用) WeightedResponseTimeRule

1.根据每个provider的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。
2.原理:一开始为轮询策略,并开启一个计时器,每30秒收集一次每个provider的平均响应时间,当信息足够时,给每个provider附上一个权重,并按权重随机选择provider,高权越重的provider会被高概率选中。

3 随机策略(不推荐) RandomRule

从provider列表中随机选择一个provider

4 最少并发数策略(应用在硬件软件环境一致的情况下) BestAvailableRule

选择正在请求中的并发数最小的provider,除非这个provider在熔断中。

5 在“选定的负载均衡策略”基础上进行重试机制 RetryRule

1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule
2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择provider不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider

6 可用性敏感策略(一般在同区域内服务集群环境中使用) AvailabilityFilteringRule

过滤性能差的provider,有2种:
第一种:过滤掉在eureka中处于一直连接失败provider
第二种:过滤掉高并发的provider

7 区域敏感性策略(应用在大型的,物理隔离分布式环境中) ZoneAvoidanceRule

1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider
2.如果这个ip区域内有一个或多个实例不可达或响应变慢,都会降低该ip区域内其他ip被选中的权重。

2 配置负载均衡策略

可以通过修改 ribbonappclient 应用的全局配置文件来改变当前环境中使用的Ribbon负载均衡策略。

server:
  port: 8081

spring:
  application:
    name: ribbon-app-client

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/


ribbon-app-service: # 远程访问这个命名的服务
  ribbon: # 底层Ribbon配置
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具体的负载均衡策略类型全名

五、 Ribbon的点对点直连

Ribbon也可以脱离Eureka Server注册中心,通过配置的方式指定要调用的远程服务信息,实现Ribbon点对点直连。修改的配置内容如下:

server:
  port: 8081

spring:
  application:
    name: ribbon-app-client

#eureka:
#  client:
#    service-url:
#      defaultZone: http://localhost:8761/eureka/


ribbon-app-service: # 远程访问这个命名的服务
  ribbon: # 底层Ribbon配置
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具体的负载均衡策略类型全名
    listOfServers: localhost:8080  # 多个地址用逗号分隔。

ribbon: # 关闭Ribbon自动访问Eureka服务端。
  eureka:
    enabled: false
上一篇:ios 中[framework] CUICatalog: Invalid asset name supplied: ''的bug解决方案


下一篇:C - Command Line Arguments