原文再续,书接上回,上一个博客讲了怎么用Eureka,这次来讲一下怎么用Ribbon做负载均衡。

        直接进入正题,基于上两次的微服务,我们做一些简单的改动就可以使用Ribbon实现负载均衡,首先要在microservicecloud-consumer-dept模块中引入Ribbon相关依赖,在pom.xml中添加如下内容:

<!-- Ribbon配置(需要依赖于eureka) -->
  	<dependency>
  		<groupId>org.springframework.cloud</groupId>
  		<artifactId>spring-cloud-starter-eureka</artifactId>
  	</dependency>
  	<dependency>
  		<groupId>org.springframework.cloud</groupId>
  		<artifactId>spring-cloud-starter-ribbon</artifactId>
  	</dependency>
  	<dependency>
  		<groupId>org.springframework.cloud</groupId>
  		<artifactId>spring-cloud-starter-config</artifactId>
  	</dependency>

        然后修改application.yml文件,修改后如下:

server:
   port: 80
   
eureka:
   client:
      register-with-eureka: false
      service-url:
         defaultZone: http://eureka1:6006/eureka,http://eureka2:6007/eureka,http://eureka3:6008/eureka

        之后在ConfigBean中添加@LoadBalanced注解:

package com.sunsy.springcloud.configuration;

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;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;

@Configuration
public class ConfigBean {

	@Bean
	@LoadBalanced
	public RestTemplate getRestTemplate() {
		return new RestTemplate();
	}
		
}

        把DeptConsumerController中的private static final String REST_URL_PREFFIX="http://localhost:8001"替换成
    private static final String REST_URL_PREFFIX="http://MICROSERVICECLOUD-DEPT"。

        在主启动类添加@EnableEurekaClient注解,至此对Consumer子工程的改造就初步完成了。

        接下来新建一个新的数据库clouddb02,里边一样建一个dept表,表结构也一样,只是数据不一样。

        接下来新建一个子工程microservicecloud-provider-dep1,把microservicecloud-provider-dept里的内容复制到新建的工程里,修改一下新建工程的application.yml,如下(和原版的只有三点不同:1.port;2.datasource.url;3.instance-id):

server:
   port: 8002

mybatis:
   config-location: classpath:mybatis/mybatis.cfg.xml
   type-aliases-package: com.sunsy.springcloud.entity
   mapper-locations: classpath:mybatis/mapper/**/*.xml

spring:
   application:
      name: microservicecloud-dept
   datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/cloudDb02
      username: root
      password: 413531
          
eureka:
   client:
      service-url:
         #defaultZone: http://192.168.44.63:6006/eureka
         defaultZone: http://eureka1:6006/eureka,http://eureka2:6007/eureka,http://eureka3:6008/eureka
   instance:
      instance-id: dept-provider-8002    #自己命名instance-id,替代默认值
      prefer-ip-address: true            #把鼠标放在status上时会在浏览器的左下角显示真实的IP和端口号

        至此就大功告成了。启动我们所有的微服务(三个EurekaServer、两个Provider、一个Consumer),之后访问http://127.0.0.1:80/consumer/dept/list,可以得到正确的json数据,刷新一下发现json数据变成了另一个数据库的数据,再刷新又变回来,再刷新再变......(也就是Ribbon默认的负载均衡策略:轮询)。

        那么我们怎么修改负载均衡策略呢?Ribbon里有几个已经实现的策略,比如轮询,比如随机,我们也可以自己写负载策略,下面说一下怎么搞。

        在Consumer子工程的com.sunsy.springcloud.configuration包下新建类ReluConfig.java,内容如下:

package com.sunsy.springcloud.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.netflix.loadbalancer.IRule;
import com.sunsy.springcloud.relu.OwnRelu;

@Configuration
public class ReluConfig {
	
	@Bean
	public IRule ownRule() {
		return new OwnRelu();
	}

}

        return new OwnRelu()里的OwnRelu类就是我们自己定义的方法(你也可以改成return new RandomRule(),这个RandomRule方法是Ribbon自己提供的,即随机负载均衡策略,就不多说了)。接下来新建一个包com.sunsy.springcloud.relu,在其下新建OwnRelu.java(模仿一下Ribbon自带的RandomRule类写一下自己的策略),如下:

package com.sunsy.springcloud.relu;

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

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

public class OwnRelu extends AbstractLoadBalancerRule {
    Random rand;

    public OwnRelu() {
        rand = new Random();
    }
    
    private int repeatValue = 0;
    private int currentIndex = 0;

    /**
     * Randomly choose from all living servers
     */
    public Server choose(ILoadBalancer lb, Object key) {
        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;
            }

            if(repeatValue<5 && currentIndex==0) {
            	server = upList.get(currentIndex);
            	repeatValue++;
            }else {
            	repeatValue=0;
            	currentIndex++;
            	if(currentIndex>=upList.size()) {
                	currentIndex=0;
                }
            	server = upList.get(currentIndex);
            }
            
            
            
            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;

    }

	@Override
	public Server choose(Object key) {
		return choose(getLoadBalancer(), key);
	}

	@Override
	public void initWithNiwsConfig(IClientConfig clientConfig) {
	
	}
}

        我们自己写的这个策略可以第一次调用provider1,然后连续调用6次,再调用provider一次,再调用provider1六次,再调用provider一次...可以重启服务访问http://127.0.0.1/consumer/dept/list验证一下是不是这样。

        如果想细化不同的微服务有不同的策略的话,可以在consumer的主启动类上添加注解@RibbonClient(name="MICROSERVICECLOUD-DEPT", configuration=ReluConfig.class)

里面的name就是微服务的名字,configuration就是要使用的策略(不过这个东西我没验证,我电脑不支持我再写俩微服务了)

        至此Ribbon的使用示例就演示结束了,想看源码的可以去我的git上看:https://github.com/ssystc/springcloud-demo。每次的博客我都会提交一个版本,可以根据commit信息找到对应的版本去看。

        下次应该会说一下feign的使用,谢谢支持点赞。。。溜了溜了