Dubbo使用了解及想法

6次阅读

共计 5039 个字符,预计需要花费 13 分钟才能阅读完成。

Dubbo

假期第二天,把 Dubbo 的一些理解整理了一下,对于大家广义上的 Dubbo 是一款分布式,高可用,负载均衡的远程调用框架,而对于做过的项目中狭义上来说,以上功能没用到过一个,属于完完全全的单机部署,所以以下只能算是个人理解,毕竟没有在生产上实际运用到分布式。


介绍 Dubbo

Dubbo 的介绍网上有很多,这里不做太多介绍,只需要知道它是一款分布式的 RPC 框架,由阿里巴巴开发并已经开源,Dubbo 官方地址,Github 地址。

而 Dubbo 的 RPC 流程,这里贴一张官方的调用流程图。

上图基本可以看出 Dubbo 的调用流程,不过第 5 点 count Monitor 这一块,应该是在最近的 Dubbo2.7.x 版本增加的,之前的 2.5.x 版本没有监控的这块逻辑。


使用 Dubbo

Dubbo 使用起来也比较方便,版本可以使用 Spring,SpringBoot,SpringCloud-Alibaba。

Dubbo 官方是推荐的 Spring,这种方式在个人看来是配置 xml 文件太多,当然方便的地方也有很多很多,举例来说直接改 xml 文件比起改代码靠谱很多。

而 SpringBoot Dubbo,使用起来没有太多的注解,代码整洁度很高,配合 Springboot 启动 Dubbo 服务,也是如虎添翼,完全可以用一句“简直不用太爽”来形容。

SpringCloud-Alibaba,作为阿里强推的 SpringCloud-Nextflix 的替代版本,只能说:嗯,不错!

下面以 Spring 版本来说明

Register

Dubbo 的调用流程如之前的图上来说,首先需要准备注册中心,官方推荐的是 Zookeeper,个人认为 Zookeeper 很不错,不过也可以了解以下 Nacos,当然没有注册中心可以先进行本地注册和调用,只需要将注册中心地址改成 N/A 就可以。

这里说明一下 Zookeeper,Zookeeper 是以节点来进行服务注册和消费,很像二叉树算法,当然很多框架和数据库都是运用了二叉树。

而我们使用 Zookeeper 时,也可以使用它的节点的特征,来进行锁的实现,将特定的唯一的 key 注册到节点,根据节点是否存在来判断是否锁定,分布式锁的问题也不会有太大的问题,只需要将 Zookeeper 配置集群,相互监听 Zookeeper 的心跳。

Provider

Provider 基本上是以接口的形式进行注册到 Zookeeper,如果看过 Zookeeper 下的节点的同学都会发现,注册路径 dubbo://com.yanzhenyidai.squid.example.SquidService 这样的形式,首先 dubbo 是协议名,其他的则是 java
项目中的包路径。

在 Zookeeper 的 bin 目录下执行 sh zkCli.sh 进入 Zookeeper 的本地客户端中,可以直观的看出 dubbo 注册节点。

Consumer

Consumer 和 Provider 设计类似,调用底层是通过 Netty 实现,首先通过获取 Zookeeper 注册的节点信息,获取开放的端口,直接请求发送数据来进行。

其他功能

泛化调用

泛化调用的存在意义在于,比如你是使用的 Java 语言,而对方调用者使用的 GO,GO 的这一方不可能按照 Java 的规范来编写接口进行调用,Dubbo 的设计还是很有前瞻性。

只需要在 <dubbo:reference> 中的 generic 设置为 true,接口就被声明成了泛化接口,而后使用 GenericService 进行 invoke,类似代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
     
   <dubbo:application name="demotest-consumer" owner="programmer" organization="dubbox"/>
   <!-- 向 zookeeper 订阅 provider 的地址,由 zookeeper 定时推送 -->
   <dubbo:registry address="zookeeper://localhost:2181"/>
   <!-- 使用 dubbo 协议调用定义好的 api.PermissionService 接口 -->
   <dubbo:reference id="permissionService" interface="com.alibaba.dubbo.demo.DemoService" generic="true"/>
</beans>
package com.alibaba.dubbo.consumer;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.alibaba.dubbo.rpc.service.GenericService;

public class Consumer {public static void main(String[] args) {
       /////////////////Spring 泛化调用 /////////  
       ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
       context.start();
       System.out.println("consumer start");
       GenericService demoService = (GenericService) context.getBean("permissionService");
       System.out.println("consumer");
       Object result = demoService.$invoke("getPermissions", new String[] {"java.lang.Long"}, new Object[]{ 1L});
       System.out.println(result);
   }
}

补充说明的是,虽然泛化看起来是很麻烦,没有 Rest 接口来的快,但是设计出来就是有用处的,真的是必不可缺的。

负载均衡

Dubbo 的负载均衡没有像 SpringCloud Robbin 那样,有专门的 LoadBanlance,看过 Dubbo-Admin 的同学应该都知道里面的服务都有一个叫做权重的概念,而 Dubbo 的负载均衡正是以权重来进行的计算。

public class RandomLoadBalance extends AbstractLoadBalance {
   public static final String NAME = "random";

   public RandomLoadBalance() {}

   protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {int length = invokers.size();
       boolean sameWeight = true;
       int[] weights = new int[length];
       int firstWeight = this.getWeight((Invoker)invokers.get(0), invocation);
       weights[0] = firstWeight;
       int totalWeight = firstWeight;

       int offset;
       int i;
       for(offset = 1; offset < length; ++offset) {i = this.getWeight((Invoker)invokers.get(offset), invocation);
           weights[offset] = i;
           totalWeight += i;
           if(sameWeight && i != firstWeight) {sameWeight = false;}
       }

       if(totalWeight > 0 && !sameWeight) {offset = ThreadLocalRandom.current().nextInt(totalWeight);

           for(i = 0; i < length; ++i) {offset -= weights[i];
               if(offset < 0) {return (Invoker)invokers.get(i);
               }
           }
       }

       return (Invoker)invokers.get(ThreadLocalRandom.current().nextInt(length));
   }
}

可以看到,Dubbo 的算法是先获取到所有服务的权重总数,然后按照每个服务的权重进行百分比的分配,而如果权重都是一样的,则直接进行随机调用。

SPI

SPI 作为 java 中设计的牛逼的拓展,你完全可以在 META-INF/services 下创建文件,一般是文件名为 service 名称,文件里加载指定的实现类,类似于笔记本可以外接键盘的场景。

而 Dubbo 的 SPI,官方说明重新定义了一套规则,在新 dubbo 的版本中,ExtensionLoader 类中,loadResource 方法,源码中确实是使用 IOC 的逻辑,需要注意的是,与 java SPI 不同,dubbo 的 SPI 文件里的
内容是以 key-value 出现。

PojoUtil

最后写一下 dubbo 的序列化机制,之前在 2.5.x 版本中被吐槽惨了,大部分请求传输量比较大的公司都进行了序列化替换。而在 PojoUtil 中,dubbo 实现的是进行 Map 和 Object 互转。如果是传输 byte 字节,只有在 <dubbo:protocol> 中将 ploy 容量增大。


部署 Dubbo

部署 Dubbo 的方式,按照服务来划分的话,部署起来很方便,单独的将服务启动,前端功能最后启动。

如果是做分布式的话,因为之前没有过类似经验,但是自己慢慢研究,得出如下方式:

将 Zookeeper 进行相互监听,最好是三台或者三台以上的奇数形式存在,大家都知道 Zookeeper 的选举机制是按照单数的形式来进行运算。Zookeeper 相互监听,而 Dubbo 服务注册的 url 地址则要分别加上所有的 Zookeeper 的地址,以 ;
结尾,例如 192.168.10.1:2181;192.168.10.2:2181,这样注册成功后,在 Dubbo-Admin 上可以看到,服务分别注册到每个 Zookeeper 上。

而上面说到的负载均衡机制,这个地方可以判断服务器配置来进行配置权重,可以使用 1 台高配置服务器,两台低配置服务器,大部分服务可以在高配置服务器上调用,一部分放到低配的服务器上。

分布式部署需要注意的是,日志文件一定要按照每天,每个文件的最大 size 进行划分,比如每个文件最大只能存储 20MB,这样查找日志起来也很方便。


Dubbo 中遇到的坑

Dubbo 中遇到的坑太多,一时间回想不起来,大体存在如下:

  • 服务重复调用,解决方案,重试机制大部分放到 provider 配置,consumer 一定配置要为 0
  • IPv4 和 IPv6,使用 java 参数配置,仅支持 IPv4 启动,毕竟现在 IPv6 还没有普及。

总结

总结下来,Dubbo 真的是用到过的最多的分布式框架,写出来的内容都是最多的,不过由于 Dubbo 用的太多,而 SpringCloud 难度比 Dubbo 要小很多,现在大部分项目都转投到了 SpringCloud 上面,后面也会出更多的 SpringCloud 相关的
使用心得文章,Dubbo 的文档大家真的可以多看官方文档,非常成熟的文档结构可以帮助使用 Dubbo 的同学更快的定位到服务问题,最主要的原因中文文档很多。

正文完
 0