-
快速启动
git clone https://github.com/apache/incubator-dubbo.git
cd incubator-dubbo
运行 dubbo-demo-provider中的com.alibaba.dubbo.demo.provider.Provider
如果使用Intellij Idea 请加上-Djava.net.preferIPv4Stack=true
-
关键配置
<?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://dubbo.apache.org/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 提供方应用信息,用于计算依赖关系 -->
<dubbo:application name="hello-world-app" />
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="zookeeper://224.5.6.7:1234" />
<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
-
启动类
public class Provider {
/**
* To get ipv6 address to work, add
* System.setProperty("java.net.preferIPv6Addresses", "true");
* before running your application.
*/
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
System.in.read(); // press any key to exit
}
}
dubbo-admin搭建-github
dubbo-admin搭建-官方文档
## java env
export JAVA_HOME=/usr/local/java/jdk1.8
export JRE_HOME=$JAVA_HOME/jre
nohup java -jar dubbo-admin.jar -Ddubbo.registry.address=zookeeper://eshop-cache01-64:2181 > /dev/null 2>&1 &
启动时检查
check="false"
集群模式配置
<dubbo:service cluster="failsafe" />
负载均衡
<dubbo:service interface="..." loadbalance="roundrobin" />
-
线程模型
接口com.alibaba.dubbo.common.threadpool.ThreadPool
直连提供者
${user.home}/dubbo-resolve.properties
本地调试时可用之,消费端配置只订阅
为本地开发环境不影响其他环境的,暂停注册功能;提供端配置
<dubbo:registry address="10.20.153.10:9090" register="false" />
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090" subscribe="false" />
<!-- 多协议配置 -->
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="rmi" port="1099" />
<!-- 使用dubbo协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" />
<!-- 使用rmi协议暴露服务 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
- 同一服务多种协议
<dubbo:protocol name="dubbo" port="20880" />
<dubbo:protocol name="hessian" port="8080" />
<!-- 使用多个协议暴露服务 -->
<dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
- 多注册中心
- 同一服务注册到多个注册中心
<!-- 多注册中心配置 -->
<dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" />
<!-- 向多个注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" />
- 同一服务引用不同注册中心
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 引用中文站服务 -->
<dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" />
<!-- 引用国际站站服务 -->
<dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" />
- 不同服务使用不同注册中心
<!-- 多注册中心配置 -->
<dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" />
<dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" />
<!-- 向中文站注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" />
<!-- 向国际站注册中心注册 -->
<dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" />
-
服务分组
当一个接口有多种实现时,可以用 group 区分。
<dubbo:service group="feedback" interface="com.xxx.IndexService" />
-
多版本
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
-
分组聚合
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" />
- 参数验证
-
结果缓存
<dubbo:reference interface="com.foo.BarService" cache="lru" />
-
泛化引用
<dubbo:reference id="barService" interface="com.foo.BarService" generic="true" />
GenericService barService = (GenericService) applicationContext.getBean("barService");
Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" });
<bean id="genericService" class="com.foo.MyGenericService" />
<dubbo:service interface="com.foo.BarService" ref="genericService" />
-
回声测试
所有服务自动实现 EchoService 接口,只需将任意服务引用强制转型为 EchoService,即可使用
// 远程服务引用
MemberService memberService = ctx.getBean("memberService");
EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
// 回声测试可用性
String status = echoService.$echo("OK");
assert(status.equals("OK"));
-
上下文信息
RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 再调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 调 C 的信息。
// 远程调用
xxxService.xxx();
// 本端是否为消费端,这里会返回true
boolean isConsumerSide = RpcContext.getContext().isConsumerSide();
// 获取最后一次调用的提供方IP地址
String serverIP = RpcContext.getContext().getRemoteHost();
// 获取当前服务配置信息,所有配置信息都将转换为URL的参数
String application = RpcContext.getContext().getUrl().getParameter("application");
// 注意:每发起RPC调用,上下文状态会变化
yyyService.yyy();
注意:path, group, version, dubbo, token, timeout 几个 key 是保留字段
// 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,
//用于框架集成,不建议常规业务使用
RpcContext.getContext().setAttachment("index", "1");
xxxService.xxx(); // 远程调用
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
<dubbo:method name="findFoo" async="true" sent="true" />
</dubbo:reference>
sent="true" 等待消息发出,消息发送失败将抛出异常。
sent="false" 不等待消息发出,将消息放入 IO 队列,即刻返回。
// 此调用会立即返回null
fooService.findFoo(fooId);
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<Foo> fooFuture = RpcContext.getContext().getFuture();
// 此调用会立即返回null
barService.findBar(barId);
// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future
Future<Bar> barFuture = RpcContext.getContext().getFuture();
// 此时findFoo和findBar的请求同时在执行,客户端不需要启动多线程来支持并行,而是借助NIO的非阻塞完成
// 如果foo已返回,直接拿到返回值,否则线程wait住,等待foo返回后,线程会被notify唤醒
Foo foo = fooFuture.get();
// 同理等待bar返回
Bar bar = barFuture.get();
// 如果foo需要5秒返回,bar需要6秒返回,实际只需等6秒,即可获取到foo和bar,进行接下来的处理。
-
本地调用
本地调用使用了 injvm 协议
<dubbo:service protocol="injvm" />
<dubbo:reference injvm="true" .../>
<dubbo:service injvm="true" .../>
<bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
<dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />
package com.foo;
public class BarServiceStub implements BarService {
private final BarService barService;
// 构造函数传入真正的远程代理对象
public (BarService barService) {
this.barService = barService;
}
public String sayHello(String name) {
// 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
try {
return barService.sayHello(name);
} catch (Exception e) {
// 你可以容错,可以做任何AOP拦截事项
return "容错数据";
}
}
}
-
本地伪装
本地伪装通常用于服务降级
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
package com.foo;
public class BarServiceMock implements BarService {
public String sayHello(String name) {
// 你可以伪造容错数据,此方法只在出现RpcException时被执行
return "容错数据";
}
}
如果服务的消费方经常需要 try-catch 捕获异常,请考虑改为 Mock 实现,并在 Mock 实现中 return null
-
延迟暴露
延迟到 Spring 初始化完成后,再暴露服务
<dubbo:service delay="-1" />
Spring 2.x 初始化死锁问题规避办法
强烈建议不要在服务的实现类中有 applicationContext.getBean() 的调用,全部采用 IoC 注入的方式使用 Spring的Bean。
如果实在要调 getBean(),可以将 Dubbo 的配置放在 Spring 的最后加载。
如果不想依赖配置顺序,可以使用 <dubbo:provider delay=”-1” />,使 Dubbo 在 Spring 容器初始化完后,再暴露服务。
如果大量使用 getBean(),相当于已经把 Spring 退化为工厂模式在用,可以将 Dubbo 的服务隔离单独的 Spring 容器。
-
并发控制
服务端:<dubbo:service interface="com.foo.BarService" executes="10" />
客户端:<dubbo:reference interface="com.foo.BarService" actives="10" />
如果 <dubbo:service> 和 <dubbo:reference> 都配了actives,<dubbo:reference> 优先
Load Balance 均衡
配置服务的客户端的 loadbalance 属性为 leastactive,此 Loadbalance 会调用并发数最小的 Provider(Consumer端并发数)。
<dubbo:reference interface="com.foo.BarService" loadbalance="leastactive" />
-
连接控制
<dubbo:provider protocol="dubbo" accepts="10" />
<dubbo:reference interface="com.foo.BarService" connections="10" />
-
延迟连接
<dubbo:protocol name="dubbo" lazy="true" />
注意:该配置只对使用长连接的 dubbo 协议生效 - 粘滞连接
- 粘滞连接用于有状态服务,尽可能让客户端总是向同一提供者发起调用
- 自动开启延迟连接
<dubbo:protocol name="dubbo" sticky="true" />
-
令牌验证
可以在全局,服务,协议级别设置
<dubbo:provider interface="com.foo.BarService" token="true" />
-
路由规则
分为条件路由规则和脚本路由规则,并且支持可扩展 -
配置规则
向注册中心写入动态配置覆盖规则 -
服务降级
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
-
优雅停机
Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的
设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭
dubbo.service.shutdown.wait=15000
- 主机绑定
-
日志适配
<dubbo:application logger="log4j" />
-
访问日志
<dubbo:protocol accesslog="true" />
<dubbo:protocol accesslog="http://10.20.160.198/wiki/display/dubbo/foo/bar.log" />
- 服务容器
- 自动加载 META-INF/spring 目录下的所有 Spring 配置。
- 配置 spring 配置加载位置:
dubbo.spring.config=classpath*:META-INF/spring/*.xml
java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j
-
ReferenceConfig 缓存
ReferenceConfig 实例很重,封装了与注册中心的连接以及与提供者的连接,需要缓存,参见 ReferenceConfigCache
ReferenceConfigCache cache = ReferenceConfigCache.getCache();
// cache.get方法中会缓存 Reference对象,并且调用ReferenceConfig.get方法启动ReferenceConfig
XxxService xxxService = cache.get(reference);
// 注意! Cache会持有ReferenceConfig,不要在外部再调用ReferenceConfig的destroy方法,导致Cache内的ReferenceConfig失效!
// 使用xxxService对象
xxxService.sayHello();
<dubbo:application ...>
<dubbo:parameter key="dump.directory" value="/tmp" />
</dubbo:application>
默认策略:
导出路径,user.home标识的用户主目录
导出间隔,最短间隔允许每隔10分钟导出一次
-
序列化
启用Kryo和FST
<dubbo:protocol name="dubbo" serialization="kryo"/>
com.alibaba.dubbo.config.ServiceConfig
com.alibaba.dubbo.config.ReferenceConfig
com.alibaba.dubbo.config.ProtocolConfig
com.alibaba.dubbo.config.RegistryConfig
com.alibaba.dubbo.config.MonitorConfig
com.alibaba.dubbo.config.ApplicationConfig
com.alibaba.dubbo.config.ModuleConfig
com.alibaba.dubbo.config.ProviderConfig
com.alibaba.dubbo.config.ConsumerConfig
com.alibaba.dubbo.config.MethodConfig
com.alibaba.dubbo.config.ArgumentConfig
-
schema 配置参考手册
注意
只有 group,interface,version 是服务的匹配条件,三者决定是不是同一个服务,其它配置项均为调优和治理参数 - 协议参考手册
- 注册中心参考手册
-
Telnet 命令参考手册
win7 --> CTRL + ] -->Enter,Enter
看到dubbo>
dubbo>help
Please input "help [command]" show detail.
status [-l] - Show status.
pwd - Print working default service.
trace [service] [method] [times] - Trace the service.
help [command] - Show help.
exit - Exit the telnet.
invoke [service.]method(args) - Invoke the service method.
clear [lines] - Clear screen.
count [service] [method] [times] - Count the service.
ls [-l] [service] - List services and methods.
log level - Change log level or show log
ps [-l] [port] - Print server ports and connections.
cd [service] - Change default service.
- 将服务接口,服务模型,服务异常等均放在 API 包中,分包原则:重用发布等价原则(REP),共同重用原则(CRP)
- 服务接口尽可能大粒度,每个服务方法应代表一个功能,而不是某功能的一个步骤,否则将面临分布式事务问题,Dubbo 暂未提供分布式事务支持
- 不建议使用过于抽象的通用接口,如:Map query(Map),这样的接口没有明确语义,会给后期维护带来不便。
- 每个接口都应定义版本号,为后续不兼容升级提供可能,如:
<dubbo:service interface="com.xxx.XxxService" version="1.0" />
- 服务接口增加方法,或服务模型增加字段,可向后兼容,删除方法或删除字段,将不兼容,枚举类型新增字段也不兼容,需通过变更版本号升级。详细参见 服务协议
- 如果是完备集,可以用
Enum
,比如:ENABLE, DISABLE
。如果是业务种类,以后明显会有类型增加,不建议用Enum
,可以用String
代替;注意升级的顺序. - 服务参数及返回值建议使用 POJO 对象,即通过 setter, getter 方法表示属性的对象
- 建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好。
如果担心性能问题,在必要时,可以通过 override 掉异常类的fillInStackTrace()
方法为空方法,使其不拷贝栈信息。 - 不要只是因为是 Dubbo 调用,而把调用 try...catch 起来。try...catch 应该加上合适的回滚边界上。
对于输入参数的校验逻辑在 Provider 端要有。如有性能上的考虑,服务实现者可以考虑在 API 包上加上服务 Stub 类来完成检验。
- 在 Provider 上尽量多配置 Consumer 端属性
<dubbo:service interface="com.alibaba.hello.api.WorldService" version="1.0.0" ref="helloService"
timeout="300" retry="2" loadbalance="random" actives="0" >
<dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" />
<dubbo:service/>
- Provider 上配置合理的 Provider 端属性
- 配置管理信息
- 配置 Dubbo 缓存文件
<dubbo:registry file=”${user.home}/output/dubbo.cache” />
这个文件会缓存注册中心的列表和服务提供者列表。有了这项配置后,当应用重启过程中,Dubbo 注册中心不可用时则应用会从这个缓存文件读取服务提供者列表的信息,进一步保证应用可靠性。 - 监控配置
- 不要使用 dubbo.properties 文件配置,推荐使用对应 XML 配置