八、Spring cloud负载均衡(Ribbon)之源码解析

  涉及到的内容有:

  • Spring Cloud Netflix Ribbon 核心接口
  • Netflix Ribbon 自动装配
  • Netflix Ribbon 配置化组件

(一)Demo构建

1、新建一个工程
  三个模块:

  • user-api:公用API
  • user-ribbon-client:客户端应用
  • user-service-provider:服务端应用

2、实现 user-ribbon-client

配置信息
application.properties

#用户 Ribbon 客户端应用
spring.application.name=spring-cloud-user-ribbon-client

#服务端口
server.port=8080

#关闭 Eureka Client,显示地通过配置方式注册 Ribbon 服务地址(未配置 Eureka 时使用)
eureka.client.enabled=false

#服务提供方名称
service.provider-name=user-service-provider
service.provider.host=localhost
service.provider.port=9090

#定义 user-service-provider Ribbon 的服务器地址
#为 RibbonLoadBalancerClient 提供服务列表
user-service-provider.ribbon.listOfServers=http://${service.provider.host}:${service.provider.port}


业务逻辑

/**
 * 用户 Ribbon Controller
 * @author 咸鱼
 * @date 2018/11/11 17:59
 */
@RestController
public class UserRibbonController {
    @Value("${service.provider-name}")
    private String serviceProviderName;
    /**
     * 负载均衡客户端
     */
    private LoadBalancerClient loadBalancerClient;
    @Autowired
    public UserRibbonController(LoadBalancerClient loadBalancerClient) {
        this.loadBalancerClient = loadBalancerClient;
    }
    @GetMapping("/index")
    private String index() throws IOException {
        User user = new User();
        user.setId(1L);
        user.setName("张三");

        //选择指定的 ServiceId
        ServiceInstance serviceInstance = loadBalancerClient.choose(serviceProviderName);
		
		//参数一:ServiceId  参数二:服务实例  参数三:负载均衡请求(这里使用lambda表达式实现)
        return loadBalancerClient.execute(serviceProviderName, serviceInstance, (instance -> {
            //服务器实例,获取 主机名(Host) 和 端口(IP)
            String host = instance.getHost();
            int port = instance.getPort();
            String url = "http://" + host + ":" + port + "/user/save";
            RestTemplate restTemplate = new RestTemplate();

            //参数一:服务提供方url  参数二:请求参数  参数三:返回值的类型
            return restTemplate.postForObject(url, user, String.class);
        }));
    }
}

编写引导类

/**
 * 注解 @RibbonClient:激活 Ribbon
 * @author 咸鱼
 * @date 2018/11/11 18:05
 */
@SpringBootApplication
@RibbonClient("user-service-provider")//指定目标应用名称
public class UserRibbonClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserRibbonClientApplication.class, args);
    }
}

3、实现 user-service-provider

配置信息
application.properties

#用户服务提供方应用信息
spring.application.name=user-service-provider

#服务端口
server.port=9090

#关闭 Eureka Client,显示地通过配置方式注册 Ribbon 服务地址(这里只是为了演示效果,一般用 Eureka)
eureka.client.enabled=false

实现 UserService

/**
 * 内存实现{@link UserService}
 * @author 咸鱼
 * @date 2018/11/11 16:39
 */
@Service
public class InMemoryUserServiceImpl implements UserService {
    private Map<Long, User> userMap = new HashMap<>();
    @Override
    public boolean saveUser(User user) {
        return userMap.put(user.getId(), user) == null;
    }

    @Override
    public List<User> findAll() {
        return new ArrayList(userMap.values());
    }
}

实现 Web 服务

/**
 * 用户服务提供方 Controller
 * @author 咸鱼
 * @date 2018/11/12 18:42
 */
@RestController
public class UserServiceProviderController {
    
    private UserService userService;

    @Autowired
    public UserServiceProviderController(UserService userService) {
        this.userService = userService;
    }
    
    @PostMapping("/user/save")
    public boolean user(@RequestBody User user){
        return userService.saveUser(user);
    }
}

编写引导类

/**
 * @author 咸鱼
 * @date 2018/11/12 18:44
 */
@SpringBootApplication
public class UserServiceProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceProviderApplication.class, args);
    }
}

4、分析 Ribbon 调动链路
  主要是分析 Ribbon 在负载均衡时如何选择服务器:
LoadBalancerClient(RibbonLoadBalancerClient) ->
ILoadBalancer(ZoneAwareLoadBalancer) ->
IRule(ZoneAvoidanceRule)

5、自定义实现 IRule

  因为是客户端类负载均衡,所以该类创建在 user-ribbon-client 模块中。
(1)扩展 AbstractLoadBalancerRule:MyRule

/**
 * 自定义{@link IRule}实现
 * @author 咸鱼
 * @date 2018/11/12 19:03
 */
public class MyRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {

    }

    @Override
    public Server choose(Object key) {
        //获取负载均衡器
        ILoadBalancer loadBalancer = getLoadBalancer();
        
        //获取所有可达服务器列表
        List<Server> servers = loadBalancer.getReachableServers();
        
        if (servers.isEmpty()){
            return null;
        }
        
        //自定义选择服务器的规则
        
        //eg:永远选择最后一台服务器
        Server targetServer = servers.get(servers.size() - 1);
        return targetServer;
    }
}

(2)将 MyRule 暴露成 Bean

通过学习 RibbonClientConfiguration 源码:

	@Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) {
		if (this.propertiesFactory.isSet(IRule.class, name)) {
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}

由 @ConditionalOnMissingBean 可知,若我们自定义实现 IRule,那么将会覆盖框架默认的 IRule 实现

/**
     * 将 {@link MyRule} 暴露成 {@link Bean}
     * @return {@link MyRule}
     */
    @Bean
    public IRule myRule(){
        return new MyRule();
    }

6、配置化实现组件
  组件主要包括:

  • IClientConfig
  • IRule
  • IPing
  • ServerList<Server>
  • ServerListFilter<Server>
  • ILoadBalancer
  • ServerListUpdater

通过源码学习 PropertiesFactory :

public PropertiesFactory() {
		classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
		classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
		classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
		classToProperty.put(ServerList.class, "NIWSServerListClassName");
		classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
	}

可知 “NFLoadBalancerClassName” 等是可以配置的

(1)自定义 IPing :MyPing

/**
 * 实现 {@link IPing} 接口:检查对象 /actuator/health 是否正常状态码:200
 * @author 咸鱼
 * @date 2018/11/12 19:51
 */
public class MyPing implements IPing {
    @Override
    public boolean isAlive(Server server) {
        String host = server.getHost();
        int port = server.getPort();
        // /health endpoint
        //通过 Spring 组件实现 URL 拼装
        UriComponentsBuilder builder = UriComponentsBuilder.newInstance();
        builder.scheme("http");
        builder.host(host);
        builder.port(port);
        builder.path("/actuator/health");
        URI uri = builder.build().toUri();

        RestTemplate restTemplate = new RestTemplate();

        //因为getForEntity()方法返回的ResponseEntity<String>对象,含有状态码
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class);

        //当响应状态码等于 200 时,返回 true, 否则返回 false
        return HttpStatus.OK.equals(responseEntity.getStatusCode());
    }
}

(2)暴露 MyPing 为 Bean
方式一:
增加配置 application.properties

#扩展 IPing 实现
user-service-provider.ribbon.NFLoadBalancerPingClassName=org.pc.ping.MyPing

方式二:

/**
     * 将 {@link MyPing} 暴露成 {@link Bean}
     * @return {@link MyPing}
     */
    @Bean
    public MyPing myPing(){
        return new MyPing();
    }

(二)Spring Cloud Netflix Ribbon 核心接口

1、LoadBalancerClient 负载均衡器客户端

(1)主要职责

  • 转化URI:将含应用名称URI转化成具体主机+端口的形式
  • 选择服务实例:通过负载算法,xuanze选择指定服务中的一台机器实例
  • 请求执行回调:针对选择后服务实例,执行具体的请求回调操作

(2)默认实现
RibbonLoadBalancerClient

(3)自动装配源
RibbonAutoConfiguration#loadBalancerClient()

2、LoadBalancerContext 负载均衡上下文

(1)主要职责

  • 转化URI:将含应用名称URI转化成具体主机+端口的形式
  • 组件关联:关联 RetryHandler、ILoadBalancer 等
  • 记录服务统计信息:记录请求响应时间、错误数量等

(2)默认实现
RibbonLoadBalancerContext

(3)自动装配源
RibbonClientConfiguration#ribbonLoadBalancerContext()

3、ILoadBalancer 负载均衡器

(1)主要职责

  • 增加服务器
  • 获取服务器:通过关联 Key 获取、获取所有服务列表、获取可用服务器列表
  • 服务器状态:标记服务器宕机

(2)默认实现
ZoneAwareLoadBalancer

(3)自动装配源
RibbonClientConfiguration#ribbonLoadBalancer()

4、IRule 规则接口

(1)主要职责

  • 选择服务器:根据负载均衡器以及关联 key 获取候选的服务器

(2)默认实现
ZoneAvoidanceRule

(3)自动装配源
RibbonClientConfiguration#ribbonRule()

5、IPing PING 策略

(1)主要职责

  • 活动检测:根据指定服务器,检测其是否活动

(2)默认实现
DummyPing

(3)自动装配源
RibbonClientConfiguration#ribbonPing()

6、ServerList 服务器列表

(1)主要职责

  • 获取初始化服务器列表
  • 获取更新服务器列表

(2)默认实现
ConfigurationBasedServerListDiscoveryEnabledNIWServerList

(3)自动装配源
RibbonClientConfiguration

(三)Netflix Ribbon 自动装配

1、RibbonAutoConfiguration

  • loadBalancerClient()
  • propertiesFactory()

2、LoadBalancerAutoConfiguration

  • @LoadBalanced
  • RestTemplate
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Cloud负载均衡Ribbon是一种客户端负载均衡工具,它可以将请求分发到多个服务提供者实例中,从而提高系统的可用性和性能。Ribbon可以根据不同的负载均衡策略来选择服务提供者,例如轮询、随机、加权轮询等。同时,Ribbon还支持服务发现和服务注册,可以自动地从服务注册中心获可用的服务实例列表。在Spring Cloud微服务架构中,Ribbon是非常重要的一部分,它可以帮助我们构建高可用、高性能的微服务系统。 ### 回答2: Spring Cloud 负载均衡Ribbon 是一个在客户端上运行的组件,可以在目标服务之间分配传入请求,以降低每个服务的负载。Ribbon 提供了多种负载均衡策略,包括简单轮询、随机、使用可用性统计数据等。 在实践中,Ribbon 可以轻松地集成到 Spring Cloud 应用中,通过配置文件中的特殊前缀“@LoadBalanced”来激活负载均衡服务。Spring Cloud 应用可以使用 Ribbon 来调用其他 REST 服务,而不用担心网络瓶颈和性能问题。 使用 Ribbon 进行负载均衡的最大好处是其透明的实现,使得客户端无需感知分配策略的变化。具体来说,当使用 Ribbon 负载均衡时,客户端可以轮流使用不同的目标服务,而无需编写特定的代码。Ribbon 为开发人员提供了一致的抽象层,以便简化分配策略和请求转发的实现。 Ribbon 作为 Spring Cloud 的核心组件之一,可以与其他重要的云基础设施服务进行集成,例如 Eureka 服务注册、Zookeeper 服务发现和 Consul 网格。通过这种方式,Ribbon 可以很容易地在多个云基础设施服务之间进行切换和部署。此外,Ribbon 还支持快速故障转移和自动重试机制,以确保应用程序可以在单个节点或整个系统故障时保持高可用性。 ### 回答3: Spring Cloud是一个基于Spring Boot实现的云原生应用开发框架,其中Spring Cloud Ribbon是其核心组件之一,提供了负载均衡的功能。 在传统的架构中,为了保证高可用性和可扩展性,常常需要多个相同的应用实例来处理用户请求,这时候就需要使用负载均衡来将请求均匀地分配到各个实例上。Spring Cloud Ribbon就是为了实现这个目的而设计的。 Ribbon可以将所有服务实例看作是一个整体,通过算法(一般为轮询算法)将请求分配给各个实例,从而实现负载均衡Ribbon内部维护了一个可用服务实例列表,当有服务实例启动或宕机时,它会实时更新列表,保证了服务的动态感知和适应。 Ribbon还提供了一些负载均衡策略,比如轮询(Round Robin)、随机(Random)、最少连接(Least Connection)等,可以根据具体业务来选择不同的策略。 在Spring Cloud中,使用Ribbon进行负载均衡非常简单,只需要加上@LoadBalanced注解,就可以实现自动的负载均衡。具体步骤如下: 1. 引入spring-cloud-starter-ribbon依赖 2. 创建RestTemplate实例,并添加@LoadBalanced注解 3. 像普通的RestTemplate一样使用其get、post等方法发起请求即可 最后,需要注意的是,Ribbon只是一种负载均衡的实现方式,而且还有其它的负载均衡框架可以选择,比如Nginx、HAProxy等。选择哪种负载均衡框架,需要根据具体业务需求、系统规模等综合考虑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值