SpringCloud 负载均衡(Ribbon)

1 负载均衡

1.1 图解

在这里插入图片描述
  上图所示,如果客户在不同时间段访问,将会导致不同服务器在某个时间段的压力会很大,而在某个时间段某些服务器又很空闲导致资源的分配不合理而影响体验。
在这里插入图片描述
  因此如上图需要有个中间件来处理请求的分发。让资源得到充分利用。这就是负载均衡,即将用户请求平躺的分配到多个服务上,从何达到系统的高可用。

1.2 分类
  • 集中式:即在服务的消费方和提供方之间使用独立的负载均衡设置,如 Nginx(方向代理服务器),由该设施负责把请求通过某种策略转发至服务的提供方。
  • 进程式
    • 将负载均衡集成到消费方,消费方从服务注册中心获知哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。
    • Ribbon 就输入进程式负载均衡,它只有一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

2 搭建服务提供者集群

  再搭建一个 9002 端口的的服务提供者,服务名和 9001 一样。(【之前项目】

2.1 application.yml
server:
  port: 9002

spring:
  application:
    name: springcloud-provider

  datasource:
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/datasource?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
    # 数据库连接池
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      #  最大连接池数量
      max-active: 20
      # 初始化连接池的数量
      initial-size: 5
      # 最小连接池 数量
      min-idle: 2
      # 这里建议配置为TRUE,防止取到的连接不可用
      test-on-borrow: true
      test-on-return: false
      # 验证连接有效与否的SQL,不同的数据配置不同
      validation-query: select
      #通过别名的方式配置扩展插件,常用的插件有:
      #监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
      filters: stat,slf4j,wall
      #  配置获取连接等待超时的时间 单位毫秒
      max-wait: 6000

# mybatis 配置
mybatis:
  # 指定 mapper.xml 的路径
  mapper-locations: classpath*:mappers/*.xml
  # mapper.xml 中的 resultType 或 resultMap 返回的实体类会自动在此包下找
  type-aliases-package: com.pky.springcloud.provider9002.domain
  # settings 下的配置
  configuration:
    # 开启驼峰命名功能 其它的一些属性参考 mybatis-config.xml中的settings属性
    map-underscore-to-camel-case: true

# eureka 配置
eureka:
  client:
    service-url:
      # eureka 服务端地址(即注册地址),若 eureka 开启了权限认证,则需要携带账号密码
#      defaultZone: http://pkyShare:123456@localhost:7001/eureka/
      defaultZone: http://pkyShare:123456@eureka7001.com:7001/eureka/,http://pkyShare:123456@eureka7002.com:7002/eureka/,http://pkyShare:123456@eureka7003.com:7003/eureka/
2.2 Controller

  修改相应信息后,在Controller 中输出对应端口号,便于确认请求分发到哪个服务端。

@RestController
@RequestMapping(value = "users")
public class UserInfoController {

    @Autowired
    UserInfoService userInfoService;

    /**
     * 通过用户名获取用户
     * @param username
     * @return
     */
    @GetMapping(value = "/{username}")
    public UserInfo getByName(@PathVariable String username) {
        System.out.println("9002");
        UserInfo userInfo = userInfoService.getByUsername(username);
        return userInfo;
    }
}
2.3 启动注册

  下图表明已注册。
在这里插入图片描述

3 Ribbon 整合

3.1 pom.xml

  在客户端(服务消费者【之前项目】)项目中添加如下依赖:

       <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-starter-ribbon</artifactId>
           <version>1.4.7.RELEASE</version>
       </dependency>
2.2 @LoadBalanced

  因为是用 RestTemplate 请求模板进行请求,因此需要在该模板上配置负载均衡:

@Configuration
public class RestConfig {

    /**
     * 配置 restTemplate 请求模板的 bean
     * @return
     */
    @Bean
    @LoadBalanced // 开启负载均衡
    public RestTemplate getTemplate() {
        return new RestTemplate();
    }
}
3.3 Controller

  修改客户端的请求地址,即将之前固定写死的 localhost:9001 改为注册在 Eureka 中服务端的服务名。这样 TestTemplate 在 Eureka 中寻找相应的服务名,然后通过 Ribbon 的负载均衡算法将请求分发到相服务端。

@RestController
@RequestMapping(value = "users")
public class UserInfoController {

    @Autowired
    RestTemplate restTemplate;
    
    // private String url = "http://localhost:9001/"	// 之前的写法
    private String url = "http://springcloud-provider/";	// 将地址改为注册在 Eureka 上服务端的服务名

    /**
     * 通过用户名获取用户
     * @param username
     * @return
     */
    @GetMapping(value = "/{username}")
    public UserInfo getByName(@PathVariable String username) {
        UserInfo userInfo = restTemplate.getForObject(url + "users/" + username, UserInfo.class);
        return userInfo;
    }
}
3.4 启动并请求

  启动后在浏览器中多次请求客户端:http://localhost:8001/users/pkyShare,可以发现在相应的控制台中轮询输出 9001、9002。这样就体现了负载均衡。

4 自定义 Ribbon 负载均衡算法

4.1 IRule

  IRule 是 Ribbon 的路由组件,很多路由算法,默认是轮询算法。
在这里插入图片描述

  • RoundRobinRule: 服务轮询,默认算法。
  • RandomRule:随机服务算法。
  • AvailabilityFilteringRule:过滤掉不可用的服务服务,对剩余的服务列表按照轮询策略进行访问算法。
  • WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用 RoundRobinRule 策略,等统计信息足够,会切换到 WeightedResponseTimeRule。
  • RetryRule:先按照 RoundRobinRule 的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
  • BestAvailableRule :会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
    ZoneAvoidanceRule:复合判断 server 所在区域的性能和 server 的可用性选择服务器。

  点击以上算法可以看到,都是继承了相应的父类,这些父类又最终实现了 IRule 接口。因此,只需要自定义一个类,然后继承相应父类,实现相应接口即可。以 RandomRule 为例。

4.2 自定义 RandomRule

  自定义 Ribbon 算法需要自定义 Ribbon 客户端,即 @ RibbonClient(name = “服务名”, configuration = “自定义算法.class”)。但是在服务启动时,【自定义算法】类不能被扫描到,因此不能和启动类在同级包下(我这里的启动类在 com.pky.springcloud.consumer)。

  在 /src/main/java 路径下新建 ribbon.router 包,创建 RouterRule 类

public class RouterRule extends AbstractLoadBalancerRule {
    public RouterRule () {
    }

    @SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
    public Server choose(ILoadBalancer lb, Object key) {
		if (lb == null) {
            return null;
        } 
        else {
            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) {
                    return null;
                }

                int index = this.chooseRandomInt(serverCount);
                server = (Server)upList.get(index);
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
                **/
            }

            return server;
        }
    }

    protected int chooseRandomInt(int serverCount) {
        return ThreadLocalRandom.current().nextInt(serverCount);
    }

    public Server choose(Object key) {
        return this.choose(this.getLoadBalancer(), key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

  在同一包下创建 RouterBeanConfig 类

@Configuration
public class RouterBeanConfig {
    @Bean
    public IRule myRouter(){
        return new RouterRule();// 默认是轮询,改为 RouterRule ()
    }

  在启动类声明

@SpringBootApplication
@EnableEurekaClient // 表明自己是个 eureka 客户端,会自动注册到 eureka 服务端中
@RibbonClient(name = "springclud-consumer",configuration = RouterBeanConfig.class)
public class ConsumerApplication {

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

  这样自定义负载均衡算法就完成了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值