SpringCloud之Ribbon负载均衡

前言

Netflix Ribbon是netflix公司的开源项目,由SpringCloud整合成SpringCloud Ribbon,其主要作用是实现分布式服务中的负载均衡;其实现需要依赖Eureka,其从Eureka注册中心获取服务清单,然后根据某种算法(比如轮询、权重、最小连接数、随机算法等)来选择调用的服务器

一、项目结构

二、创建产品服务

2.1 创建goods-server-9200和goods-server-9201

2.2 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>goods-server</artifactId>
        <groupId>com.wj</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>goods-server-9200</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.wj</groupId>
            <artifactId>cloud-common-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

2.3 配置文件

2.3.1 9200配置文件

# 端口号配置
server:
  port: 9200

# 应用名称配置
spring:
  application:
    name: goods-server

#eureka客户端配置
eureka:
  instance:
    # 向注册中心发送心跳间隔时间,告诉注册中心自己还活着;默认时间是30s
    lease-renewal-interval-in-seconds: 10
    # 如果30秒内没有向注册中心发送心跳,代表发生故障,从注册中心移除掉,默认时间是90s
    lease-expiration-duration-in-seconds: 30
    #告诉服务端,服务实例以ip作为连接,而不是机器名,默认是false
    prefer-ip-address: true
    #告诉服务端,服务实例的名称
    instance-id: ${spring.application.name}-9200
  client:
    #注册中心连接地址
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/,http://localhost:8762/eureka/

2.3.2 9201配置文件

# 端口号配置
server:
  port: 9201

# 应用名称配置
spring:
  application:
    name: goods-server

#eureka客户端配置
eureka:
  instance:
    # 向注册中心发送心跳间隔时间,告诉注册中心自己还活着;默认时间是30s
    lease-renewal-interval-in-seconds: 10
    # 如果30秒内没有向注册中心发送心跳,代表发生故障,从注册中心移除掉,默认时间是90s
    lease-expiration-duration-in-seconds: 30
    #告诉服务端,服务实例以ip作为连接,而不是机器名,默认是false
    prefer-ip-address: true
    #告诉服务端,服务实例的名称
    instance-id: ${spring.application.name}-9201
  client:
    #注册中心连接地址
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka/,http://localhost:8762/eureka/

2.4 代码实现

2.4.1 启动类

package com.wj.goods.server9200;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author wangjian
 */
@EnableEurekaClient
@SpringBootApplication
public class GoodsServer9200Application {
    public static void main(String[] args) {
        SpringApplication.run(GoodsServer9200Application.class,args);
    }
}

2.4.2 实体类

package com.common.api.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

/**
 * 产品实体类
 * @author wangjian
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Goods implements Serializable {

    //id
    private String id;

    //产品名称
    private String name;

    //描述
    private String desc;

}

2.4.3 controller

package com.wj.goods.server9200.controller;
import com.common.api.base.Result;
import com.common.api.entity.Goods;
import com.wj.goods.server9200.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 产品controller
 * @author wangjian
 */
@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    /**
     * 获取产品列表
     * @return
     */
    @GetMapping("/list")
    public Result<List<Goods>> list(){
        List<Goods> list = goodsService.list();
        return Result.ok("9200服务查询产品列表成功!",list);
    }

}

2.4.4 service

package com.wj.goods.server9200.service.impl;

import com.common.api.entity.Goods;
import com.wj.goods.server9200.service.GoodsService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * 产品service层实现
 * @author wangjian
 */
@Service
public class GoodsServiceImpl implements GoodsService {

    /**
     * 查询产品列表
     * @return
     */
    @Override
    public List<Goods> list() {
        List<Goods> list = new ArrayList<>();
        list.add(new Goods(UUID.randomUUID().toString(),"华为笔记本","i5+16G+256SSD+1T"));
        list.add(new Goods(UUID.randomUUID().toString(),"联想笔记本","i7+16G+512SSD+1T+Max150"));
        list.add(new Goods(UUID.randomUUID().toString(),"华硕笔记本","i5+8G+256SSD+1T"));
        return list;
    }
}

三、修改portal-server服务

其他配置请看:SpringCloud之Eureka注册中心

3.1 增加PortalConfig

package com.wj.portal.server.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 门户配置类
 * @author wangjian
 */
@Configuration
public class PortalConfig {

    /**
     * 注入RestTemplate
     * @LoadBalanced:Ribbon实现负载均衡
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

}

3.2 增加GoodsUrlConstant

package com.wj.portal.server.constants;

/**
 * 产品接口常量类
 * @author wangjian
 */
public class GoodsUrlConstant {
    public static String GOODS_LIST_URL = "http://goods-server/goods/list";
}

3.3 增加RibbonController

package com.wj.portal.server.controller;

import com.wj.common.api.base.Result;
import com.wj.common.api.entity.Goods;
import com.wj.portal.server.constants.GoodsUrlConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

/**
 * ribbion负载均衡
 * @author wangjian
 */
@RestController
@RequestMapping("/ribbon")
public class RibbonController {

    //注入RestTemplate
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取产品列表
     * @return
     */
    @GetMapping("/goods/list")
    public Result<List<Goods>> list(){
        ResponseEntity<Result> forEntity = restTemplate.getForEntity(GoodsUrlConstant.GOODS_LIST_URL, Result.class);
        Result result = forEntity.getBody();
        return result;
    }

}

四、postman测试结果

五、自定义负载均衡策略

5.1 IRule接口

Ribbon负载均衡策略有IRule接口定义,下面是结构图

负载均衡策略含义:

  • RandomRule:随机策略
  • WeightedResponseTimeRule:根据平均响应时间计算权重,响应时间越快权重越大;如服务刚启动信息统计不足,则使用RoundRobinRule策略,等统计信息足够,切换到WeightedResponseTimeRule策略
  • AvailabilityFilteringRule:先过滤掉多次访问故障和并发数超过阀值的服务,在剩下的服务中进行轮询访问
  • ZoneAvoidanceRule:综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务
  • BestAvailableRule:过滤掉多次访问故障服务,然后选择一个并发量最小的服务
  • RetryRule:先按照RoundRobinRule策略分发,如分发到的服务不能访问,则在指定时间内进行重试,还不行,重新分发其他可用的服务;

5.2 自定义负载均衡策略

5.2.1 MyRandomRule

package com.wj.portal.server.config;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

import java.util.List;
import java.util.Random;

/**
 * 自定义随机负载均衡策略
 * @author wangjian
 */
public class MyRandomRule extends AbstractLoadBalancerRule {
    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object key) {
        System.out.println("MyRandomRule负载均衡策略..........");
        //获取ILoadBalancer
        ILoadBalancer lb = getLoadBalancer();
        if (lb == null) {
            return null;
        }
        Server server = null;

        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();

            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes
                 * only get more restrictive.
                 */
                return null;
            }

            int index =  new Random().nextInt(serverCount);
            server = upList.get(index);

            if (server == null) {
                /*
                 * The only time this should happen is if the server list were
                 * somehow trimmed. This is a transient condition. Retry after
                 * yielding.
                 */
                Thread.yield();
                continue;
            }

            if (server.isAlive()) {
                return (server);
            }

            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
        return server;
    }
}

 5.2.2 PortalConfig 增加自定义策略注入

package com.wj.portal.server.config;

import com.netflix.loadbalancer.IRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 门户配置类
 * @author wangjian
 */
@Configuration
public class PortalConfig {

    /**
     * 注入RestTemplate
     * @LoadBalanced:Ribbon实现负载均衡
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

    /**
     * 注入自定义的负载均衡策略
     * @return
     */
    @Bean
    public IRule getIRule(){
        return new MyRandomRule();
    }

}

总结

Ribbon原理解析参考:深入理解Ribbon之源码解析

文章内容有借鉴网络内容,如有侵权,请联系!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值