Dubbo高可用之负载均衡

本文在 Dubbo 2.7 整合Spring(xml形式)  上进行演示。

代码修改

dubbo-samples-xml-api模块

修改OrderService接口:

public interface OrderService {
   Order getOrderInfo(long orderId);

   List<Order> listAll();
}

dubbo-samples-xml-provider模块

1)修改OrderServiceImpl.java

@Service("orderServiceImpl")
public class OrderServiceImpl implements OrderService {

    private static Map<Long, Order> orderMap = new HashMap();

    static {

        for(long i=1; i<=100; i++){
            Order order = new Order();
            order.setOrderId(i);
            order.setOrderName("MacBook Pro " + i);
            orderMap.put(i, order);
        }
    }

    @Override
    public Order getOrderInfo(long orderId) {
        RpcContext rpcContext = RpcContext.getContext();
        System.out.println("当前调用的服务端口:" + rpcContext.getLocalPort() + ", 获取订单:" + orderId);
        return orderMap.get(orderId);
    }

    @Override
    public List<Order> listAll() {
        RpcContext rpcContext = RpcContext.getContext();
        System.out.println("当前调用的服务端口:" + rpcContext.getLocalPort() + ", 查询所有的订单列表");
        return new ArrayList(orderMap.values());
    }
}

2)dubbo-provider.xml

协议端口改为-1,随机分配一个未用的端口,这样我们就可以启动多个服务提供者了。

<!--port设置为-1,分配一个未用的端口-->
<dubbo:protocol name="dubbo" port="-1"/>

如果在idea中调试,记得勾上这个:

dubbo-samples-xml-consumer模块

public class ConsumerStarter {

    public static void main(String[] args) throws IOException {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring/dubbo-consumer.xml"});
        context.start();
        System.out.println("consumer start.....");
        //dubbo
        OrderService orderService1 = context.getBean("orderService", OrderService.class);
        System.out.println("接口:getOrderInfo");
        Random rm = new Random();
        for (int i = 0; i < 23; i++) {
            //随机生成一个1~100的数
            int orderId = rm.nextInt(100) + 1;
            System.out.println("index " + i + " SUCCESS: got getOrderInfo " + orderService1.getOrderInfo(orderId));
        }
        System.out.println("接口:listAll");
        for (int i = 0; i < 3; i++) {
            System.out.println("index " + i + " SUCCESS: got listAll " + orderService1.listAll());
        }
    }
}

负载均衡策略

在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。

1)Random LoadBalance

随机法,是随机选择一台服务器来分配任务。它保证了请求的分散性达到了均衡的目的。同时它是没有状态的,不需要维持上次的选择状态和均衡因子。但是随着任务量的增大,它的效果趋向轮询后也会具有轮询算法的部分缺点。

根据随机算法,将请求随机分配到后端服务器中,请求的均匀请求依赖于随机算法,该实现方式较为简单,常常可以配合处理一些极端的请求,例如热点请求情况。不适合对命中率有要求的场景。

2)RoundRobin LoadBalance

轮询法,就是将用户的请求轮流分配给服务器,就像是挨个数数,轮流分配。这种算法比较简单,他具有绝对均衡的优点,但是也正是因为绝对均衡它必须付出很大的代价,例如它无法保证分配任务的合理性,无法根据服务器承受能力来分配任务。

轮询的实现方式比较简单,就是将请求以此分发到后端服务器中,将所有的请求均匀分配,均匀分配的坏处是通常后台服务器性能有差异,有时候希望性能较好的服务器可以多承担些请求,该策略无法满足需求。这个不适合对长连接和命中率有要求的场景。

3)LeastActive LoadBalance

最小连接法,将任务分配给此时具有最小连接数的节点,因此它是动态负载均衡算法。一个节点收到一个任务后连接数就会加1,当节点故障时就将节点权值设置为0,不再给节点分配任务。

通过活动来估计后端服务器的负载,把请求分配给活动连接数最小的后端服务器,算法比较智能,但是需要额外的资源维护后端服务器的连接列表。

最小连接法适用于各个节点处理的性能相似时。任务分发单元会将任务平滑分配给服务器。但当服务器性能差距较大时,就无法达到预期的效果。因为此时连接数并不能准确表明处理能力,连接数小而自身性能很差的服务器可能不及连接数大而自身性能极好的服务器。所以在这个时候就会导致任务无法准确的分配到剩余处理能力强的机器上。

4)ConsistentHash LoadBalance

Hash哈希是根据Source IP、 Destination IP、URL、或者其它,算hash值或者md5,再采用取模,相同的请求会请求到同一个后端服务器中。该算法无法解决热点请求,会把某个时间段的热点请求路由到某个单机上,造成雪崩效应,同时在扩充和节点宕机时发生命中率急剧降低的问题(hash算法导致),该策略适合维护长连接和提高命中率。Consistanct Hash是对Hash 算法的优化,可以有效的解决宕机和扩充造成的命中率急剧降低的问题。

一致性Hash算法也是使用取模的方法,只是,刚才描述的取模法是对服务器的数量进行取模,而一致性Hash算法是对2^32取模,什么意思呢?简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0~2^32-1(即哈希值是一个32位无符号整形)。

整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到2^32-1,也就是说0点左侧的第一个点代表2^32-1, 0和2^32-1在零点中方向重合,我们把这个由2^32个点组成的圆环称为Hash环。

下一步将各个服务器使用Hash进行一个哈希,具体可以选择服务器的IP或主机名作为关键字进行哈希,这样每台机器就能确定其在哈希环上的位置。

dubbo负载均衡配的属性值:

  • random:随机。
  • roundrobin:轮询。
  • leastactive:最少活跃调用数。
  • consistenthash:一致性Hash。

负载均衡演示

1)启动Zookeeper服务

2)修改dubbo-provider.xml

<!--服务级别-->
<!-- <dubbo:service interface="com.harvey.samples.client.OrderService" ref="orderServiceImpl"
                    protocol="dubbo" group="one" loadbalance="random"/>-->


<!--方法级别-->
<!-- <dubbo:service interface="com.harvey.samples.client.OrderService" ref="orderServiceImpl"
                    protocol="dubbo" group="one" loadbalance="random">
         <dubbo:method name="getOrderInfo" loadbalance="random"/>
         <dubbo:method name="listAll" loadbalance="roundrobin"/>
     </dubbo:service>-->

<!--一致性hash-->
<dubbo:service interface="com.harvey.samples.client.OrderService" ref="orderServiceImpl"
               protocol="dubbo" group="one" loadbalance="consistenthash">
    <!--细粒度的配置优先级更高-->
    <dubbo:method name="getOrderInfo" loadbalance="roundrobin"/>
    <!--缺省只对第一个参数 Hash-->
    <dubbo:parameter key="hash.arguments" value="0,1"/>
    <!--缺省用 160 份虚拟节点-->
    <dubbo:parameter key="hash.nodes" value="320"/>
</dubbo:service>

负载均衡可以再服务提供者中配置也可以在服务消费者中配置:

<dubbo:reference interface="..." loadbalance="roundrobin" />

3)启动两个服务提供者Provider

4)启动服务消费者进行远程调用,输出日志

可以通过配置weight来配置权重。

 

posted @ 2022-03-28 14:52  残城碎梦  阅读(114)  评论(1编辑  收藏  举报