共计 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 的同学更快的定位到服务问题,最主要的原因中文文档很多。