1.搭建zookeeper集群,搭建过程参考网上资料(地址为192.168.200.141:2182,192.168.200.141:2183,192.168.200.141:2184)。
2.搭建dubbo服务生产者和消费者项目工程,github地址:GitHub - kickTec/dubboDemo: dubbo负载均衡demo,详细过程请参考3.Dubbo2.5.3快速启动Hello World - 静候东风 - 博客园。
dubbo服务提供方项目工程结构:
dubbo服务提供方spring配置:
服务提供方通过main方法启动。
package com.kenick.main;
import com.kenick.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
// spring环境初始化,同时通过spring将服务注册到zookeeper上
System.out.println("spring环境初始化!");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-provider-dev.xml"});
context.start();
try {
// 测试服务注入到spring中是否成功
HelloService helloService = context.getBean("helloService", HelloService.class);
String ret = helloService.hello("kenick-provider");
System.out.println(ret);
System.in.read(); // 程序等待输入卡住
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务消费方项目工程目录:
服务消费方spring配置文件:
服务消费方配置代码:
package com.kenick.main;
import com.kenick.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
try {
// spring环境初始化
System.out.println("开始spring环境初始化!");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-consumer-dev.xml"});
context.start();
int provider1Sum = 0;
int provider2Sum = 0;
for(int i=0;i<2000;i++){
// 开始通过spring和dubbo获取远程服务实例
HelloService helloService = context.getBean("helloService", HelloService.class);
// 调用提供的服务hello
String ret = helloService.hello("kenick-consumer");
System.out.println(ret);
// 统计调用次数
if(ret.contains("provider1")){
provider1Sum += 1;
}else if(ret.contains("provider2")){
provider2Sum += 1;
}
System.out.println("provider1Sum:"+provider1Sum+","+provider1Sum*1.0/(provider1Sum+provider2Sum));
System.out.println("provider2Sum:"+provider2Sum+","+provider2Sum*1.0/(provider1Sum+provider2Sum));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.dubbo默认随机负载均衡-random
服务提供方配置
服务消费方调用2000次,分配为999:1001,基本为1:1。
4.dubbo负载均衡-roundrobin
两个服务提供方都配置为roundrobin,权重都为100,如下:
服务消费方调用2000次,其中提供方1、提供方2为1000:1000,比较平均。
将服务提供者2的权重调整到300(服务提供者1权重:100),服务提供2:服务提供1为1724:276,约等于6.25:1,权重起了一点作用,但比例不怎么匹配。
5.dubbo负载均衡-leastactive
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差;使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
服务消费者配置(timeout超时,actives:Activity最大的并发数,如果超过这个并发数,在invoke时将会等待)。
同时配置服务提供者1的服务sleep(模拟网络不好,减少同一时间内的调用次数)。
在服务消费方这边需要使用多线程测试调用情况,代码如下:
package com.kenick.main;
import com.kenick.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.locks.ReentrantLock;
public class Main1 {
public static void main(String[] args) {
try {
// spring环境初始化
System.out.println("开始spring环境初始化!");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"dubbo-consumer-dev.xml"});
context.start();
// 启动两个线程调用服务
ServiceInvokeRunnable serviceInvokeRunnable1 = new ServiceInvokeRunnable(context);
ServiceInvokeRunnable serviceInvokeRunnable2 = new ServiceInvokeRunnable(context);
Thread thread1 = new Thread(serviceInvokeRunnable1, "thread1");
Thread thread2 = new Thread(serviceInvokeRunnable2, "thread2");
thread1.start();
thread2.start();
}catch (Exception e){
e.printStackTrace();
}
}
static class ServiceInvokeRunnable implements Runnable{
private static Integer invokeTimes = 2000; // 总调用次数
private ClassPathXmlApplicationContext context; // spring上下文
private static ReentrantLock locker = new ReentrantLock(); // 锁住调用次数
private Integer provider1Sum = 0; // 调用服务提供者1次数
private Integer provider2Sum = 0;// 调用服务提供者2次数
public ServiceInvokeRunnable(ClassPathXmlApplicationContext context){
this.context = context;
}
public void run() {
// 多线程处理调用次数
while(invokeTimes >0){
locker.lock();
try {
if(invokeTimes>0){
invokeTimes--;
}else{
break;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
locker.unlock();
}
// 调用提供的服务hello
String threadName = Thread.currentThread().getName();
HelloService helloService = context.getBean("helloService", HelloService.class);
String ret = helloService.hello("kenick-consumer");
// 统计调用次数
if(ret.contains("provider1")){
provider1Sum++;
}else if(ret.contains("provider2")){
provider2Sum++;
}
System.out.println(threadName+":"+"provider1Sum:"+provider1Sum+","+provider1Sum*1.0/(provider1Sum+provider2Sum));
System.out.println(threadName+":"+"provider2Sum:"+provider2Sum+","+provider2Sum*1.0/(provider1Sum+provider2Sum));
}
}
}
}
服务消费方调用2000次,线程2调用服务提供方1 153次,服务提供方2 868次;线程1调用服务提供方1 160次,线程2调用服务提供方819次;两个线程调用服务提供方1总次数为313次,调用服务提供方2总次数为1687次,负载均衡策略起到了相应的作用。
6.ConsistentHash LoadBalance 一致性Hash
相同参数的请求总是发到同一提供者;当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。