相信有很多小伙伴都知道,dubbo是一个分布式、高性能、透明化的RPC服务框架,提供服务自动注册、自动发现等高效服务治理方案,dubbo的中文文档也是非常全的,中文文档可以参考这里dubbo.io。由于官网的介绍比较简洁,我这里打算通过Maven多模块工程再给小伙伴们演示一下用法。
节点角色说明
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。
集群容错模式
可以自行扩展集群容错策略,参见:集群扩展
Failover Cluster
失败自动切换,当出现失败,重试其它服务器 1。通常用于读操作,但重试会带来更长延迟。可通过 retries="2"
来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2"
来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息。
集群模式配置
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
这里我还是以dubbo官方文档中的例子作为基准,我们来详细的看看运行过程。
创建一个Maven工程
IntelliJ中创建Maven工程的方式我这里就不再多说了,这里只说一点,工程创建成功之后,将src目录删除,因为我们不需要在这个工程下面写代码,我们将以这个工程为父工程,然后给它创建多个模块。
向创建好的工程中添加模块
当我们第一步成功创建了要给Maven工程之后,第二步我们就向这个Maven工程中添加三个模块,分别是common,provider和consumer三个模块,添加完成之后效果如下:
provider将作为我们的服务提供者,consumer将作为服务消费者,这两个好理解,除了这两个之外我们还需要要给common模块,common模块主要是提供公共接口,供服务提供者和服务消费者使用。
向common模块中添加接口
在common模块中,添加一个SayHello接口,如下:
provider模块依赖common并提供服务
1.首先打开provider的pom.xml文件,在其中添加依赖,要添加的依赖有如下四个小类:
1.添加对common模块的依赖
2.添加对spring的依赖
3.添加对dubbo的依赖
4.添加对zookeeper的依赖
如下:
<dependencies>
<dependency>
<groupId>org.sang</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<artifactId>netty</artifactId>
<groupId>org.jboss.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
</dependencies>
然后在provider中实现common模块的接口,如下:
public class SayHelloImpl implements SayHello {
public String sayHello(String name) {
return "Hello "+name;
}
}
然后我们需要在provider的spring配置文件中暴露服务,如下:
<?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="hello-world-app"></dubbo:application>
<!--<dubbo:registry address="multicast://224.5.6.7:2181"/>-->
<dubbo:registry address="zookeeper://192.168.248.128:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="org.sang.SayHello" ref="sayHelloImpl"/>
<bean id="sayHelloImpl" class="org.sang.SayHelloImpl"/>
</beans>
这里我采用了dubbo推荐的注册中心zookeeper,关于Linux上zookeeper的安装小伙伴们可以参考Linux上安装Zookeeper以及一些注意事项。
注册地址就是你安装zookeeper的服务器地址,然后将服务的接口暴露出来即可。
最后我们采用一个main方法将provider跑起来,如下:
public class Main {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.start();
System.in.read();
}
}
OK,如此之后我们的provider模块就算开发完成了。
在consumer模块中消费服务
首先在consumer模块中添加相关依赖,要依赖的东西和provider的依赖一样,这里我就不再重复贴出代码。
然后我们在consumer的spring配置文件中订阅服务,订阅方式如下:
<?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="consumer-of-helloworld-app"/>
<!--<dubbo:registry address="multicast://224.5.6.7:2181" check="false"/>-->
<dubbo:registry address="zookeeper://192.168.248.128:2181" check="false"/>
<dubbo:reference id="sayHello" interface="org.sang.SayHello" check="false"/>
</beans>
首先订阅地址依然是zookeeper的地址,然后注册一个SayHello的bean,这个bean可以直接在我们的工程中使用。
一样,我们还是通过一个main方法来启动服务消费端:
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
SayHello sayHello = (SayHello) ctx.getBean("sayHello");
String s = sayHello.sayHello("张三");
System.out.println(s);
}
}
运行结果如下:
Ok,至此,一个简单的案例就完成了,有问题欢迎小伙伴留言讨论。
如果测试和开发公用一个zookeeper,。dubbo这边配置不同的group就好了
- 连接个数:单连接
- 连接方式:长连接
- 传输协议:TCP
- 传输方式:NIO异步传输
- 序列化:Hessian二进制序列化
- 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用dubbo协议传输大文件或超大字符串。
- 适用场景:常规远程服务方法调用
因为服务的现状大都是服务提供者少,通常只有几台机器,
而服务的消费者多,可能整个网站都在访问该服务,
比如Morgan的提供者只有6台提供者,却有上百台消费者,每天有1.5亿次调用,
如果采用常规的hessian服务,服务提供者很容易就被压跨,
通过单一连接,保证单一消费者不会压死提供者,
长连接,减少连接握手验证等,
并使用异步IO,复用线程池,防止C10K问题。
在dubbo RPC中,同时支持多种序列化方式,例如:
- dubbo序列化:阿里尚未开发成熟的高效java序列化实现,阿里不建议在生产环境使用它
- hessian2序列化:hessian是一种跨语言的高效二进制序列化方式。但这里实际不是原生的hessian2序列化,而是阿里修改过的hessian lite,它是dubbo RPC默认启用的序列化方式
- json序列化:目前有两种实现,一种是采用的阿里的fastjson库,另一种是采用dubbo中自己实现的简单json库,但其实现都不是特别成熟,而且json这种文本序列化性能一般不如上面两种二进制序列化。
- java序列化:主要是采用JDK自带的Java序列化实现,性能很不理想。
在通常情况下,这四种主要序列化方式的性能从上到下依次递减。对于dubbo RPC这种追求高性能的远程调用方式来说,实际上只有1、2两种高效序列化方式比较般配,而第1个dubbo序列化由于还不成熟,所以实际只剩下2可用,所以dubbo RPC默认采用hessian2序列化。
但hessian是一个比较老的序列化实现了,而且它是跨语言的,所以不是单独针对java进行优化的。而dubbo RPC实际上完全是一种Java to Java的远程调用,其实没有必要采用跨语言的序列化方式(当然肯定也不排斥跨语言的序列化)。
最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:
- 专门针对Java语言的:Kryo,FST等等
- 跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等
这些序列化方式的性能多数都显著优于hessian2(甚至包括尚未成熟的dubbo序列化)。
有鉴于此,我们为dubbo引入Kryo和FST这两种高效Java序列化实现,来逐步取代hessian2。
其中,Kryo是一种非常成熟的序列化实现,已经在Twitter、Groupon、Yahoo以及多个著名开源项目(如Hive、Storm)中广泛的使用。而FST是一种较新的序列化实现,目前还缺乏足够多的成熟使用案例,但我认为它还是非常有前途的。
在面向生产环境的应用中,我建议目前更优先选择Kryo。
启用Kryo和FST
使用Kryo和FST非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:
<dubbo:protocol name="dubbo" serialization="kryo"/>
<dubbo:protocol name="dubbo" serialization="fst"/>
参考资料:http://dubbo.io