Dubbo 是使用Spring 的 Shema 进行扩展标签和解析配置,所以我们能像 Spring 的 XML 配置方式一样进行配置。
(1)reference method > serivice method > reference > service > consumer > service。总结下来是:方法级优先,接口次之,全局再次之。级别一样,消费者优先,提供者次之。
(2)Best Practice:由服务提供者设置超时。消费者不需要关心超时时间。
(1)Dubbo 会自动加载classpath 根目录下的 dubbo.properties 文件。
(2)
dubbo.application,name=dubbo-server
dubbo.application.owner=test
dubbo.registry.address=zookeeper://127.0.0.1:2181
(3)属性配置遵循以下约定:
将 XML 配置的标签+属性名,用点分割,多个属性拆成多个行。
e.g.
dubbo:protocol.name=dubbo 和 dubbo:protocol.port=20880
(4)XML 配置的优先级 > 属性配置
我们可以通过代码调用 Dubbo 的 API 进行配置,一般用于 Test, Mock 等。
Dubbo 在 2.5.7 版本之后新增注解方式配置,更像SpringBoot 配置。
(1) 使用 java config 形式配置公共模块:
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DubboConfiguration {
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-server");
return applicationConfig;
}
@Bean
RegistryConfig registryConfig() {
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
registryConfig.setClient("curator");
return registryConfig;
}
}
(2)使用Service 注解暴露服务:
import com.alibaba.dubbo.config.annotation.Service;
import com.bill.api.IHelloService;
@Service(timeout = 5000)
public class HellServiceImpl implements IHelloService {
@Override
public String sayHello(String name) {
return name;
}
}
(3)指定 Dubbo 的扫描路径:
import com.alibaba.dubbo.config.spring.context.annotation.DubboComponentScan;
@DubboComponentScan(basePackages = "com.bill.service")
public class ServerApp {
// springboot 启动
}
(1)使用 Java Cofig 形式配置公共模块:
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DubboConfiguration {
@Bean
public ApplicationConfig applicationConfig() {
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-client");
return applicationConfig;
}
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setTimeout(3000);
return consumerConfig;
}
}
(2)使用 Reference 注解引用服务:
import com.alibaba.dubbo.config.annotation.Reference;
import com.bill.api.IHelloService;
public class ClientService {
@Reference
public IHelloService helloService;
public void doSomething() {
helloService.sayHello("helloWorld");
}
}
(3)指定 Dubbo 的扫描路径:
@DubboComponentScan(basePackages = "com.bill.service")
public class ServerApp {
// springboot 启动
}
(1)Zookeeper 注册中心。 ZK 是一个数型的目录服务,为分布式应用提供一致性服务。(目前生产推荐使用方式) (1)在服务中心注册后,如何提供服务给消费者,使用 dubbo:service/ 标签进行服务暴露。 (1)在默认情况下,使用同步方式远程调用。如果想使用一步方式,设置 async="true" (1) oninvoke() : 发起远程调用前触发。 (4) 异步回调:async=true onreturn=“xxxx” (1)Dubbo 的服务提供者有两种线程池类型:I/O处理线程池,业务调度线程池。 (1)**线程数设置过少的问题。**适当增加线程数解决。Dubbo 默认是200 个线程。 (1)在消费者和提供者之间默认只会建立一个TCP长连接。生产上,不建议在消费端设置 connections 属性 去增加连接数。 如果单个服务节点因为故障无法提供服务,可以根据配置的**集群容错模式,**调用其他的可用的服务节点,提高服务的可用性。 (1)服务者配置: (1)Failover (默认) (1)服务端: (2)纵向分层 (3)SPI (Service Provider Interface) 是java 提供的一种服务加载方式。在Jar 包META-INF/Services/中创建一个用接口命名的文件。 Dubbo 的配置文件采用Spring schema 形式扩展的。 (2)编写XSD文件 (3)编写 NamespaceHandler 和 BeanDefinitionParser来解析上面的文件。 } (4) 编写spring.handlers 和 spring.schemas 来串联所有部件。 spring.schemas (5)在spring bean 文件中应用 (1)ServiceBean 是Dubbo配置标签 参见我的集群容错和负载均衡源码学习。 (1)比如调用helloService.sayHello 方法。消费者配置消费的接口, zk 地址。
(2)ZK 集群模式配置: 2.1.1 Dubbo 如何在ZK 中建立目录结构
2.2 服务暴露
(2)
(3)使用 execute是限制服务并发量,不要超过服务器承载能力:
或者限定接口中某个方法:
(4)Best Practice : 如果线程数据超过给定的值会报异常:
RejectedExecutionException: Thread pool is exhausted!
(5)为了保证服务的稳定性,除了限制并发线程,还可以限制服务端的连接数:
2.3 引用服务
2.3.1 异步方式调用
异步调用是可以设置是否需要等待发送或返回值:
(2) sent=“true”: 等待消息发出,发送失败抛异常。
(3) sent=“false”: 不等待消息发出,将消息放在I/O队列,即刻返回。
(4)return=“falsue”: 完全忽略返回。2.3.2 远程调用回调函数
(2)onreturn():远程调用之后的回调事件。
(3) onthrow(): 远程调用出现异常时触发。在该事件中发现服务降级public class NotifyImpl implements INotify {
@Override
public void onreturn(String resStr, String inStr) {
//do something
}
@Override
public void onthrow(Throwable ex, String inStr) {
// do something
}
}
同步回调 : async=false onreturn=“xxx”
异步无回调:async=true
同步无回调:async=false4. Dubbo 中高效的I/O 线程模型
4.1 对 Dubbo I/O 模型的分析
(2)I/O 线程数是核数+1,服务调用的线程数默认是200。
(3)如果事务处理能迅速处理,直接在I/O上处理。如果事务处理慢,或者要发起新的I/O, 比如查询数据库,就派发到事务线程池中处理。4.2 Dubbo 中线程配置相关参数
参见 http://dubbo.apache.org/zh-cn/docs/user/demos/thread-model.html4.3 在Dubbo 线程方面踩过的坑
Caused by: java.util.concurrent.RejectExecutionException:thread pool is EXHAUSTED!
(2)**线程数设置太多。**线程数设置太多,会受Linux 用户线程数(默认1024)的限制, 通常用 ulimit -u 解决。
java.lang.OutOfMemoryError: unable to create new native thread.
(3)**连接不上服务器。**可能是超过了服务端最大允许的连接数。可以通过设置 accepts 值解决。4.4 对Dubbo 线程池使用的建议
(2)I/O 线程是异步读取数据,所以它消耗更多的CPU资源,默认为CPU个数+1 比较合理。
(3)数据在被读取和反序列化后,会交给业务线程池处理,业务线程池没有设置等待队列,失败后快速重试其他服务提供者,而不是排队。5. 集群的容错机制与负载均衡
5.1 集群容错机制的原理
5.2 集群容错模式的配置
(2)服务消费者
5.3 六种集群容错模式
在调用失败后会自动切换,尝试调用其他节点。一些幂等性操作,比如读操作可以选择这个模式。
在服务端:
在消费端:
在方法级别配置:
dubbo:reference
(2)FailFast
如果调用失败,忽略失败的调用,记录失败的调用到日志文件中。
(3)FailBack
记录失败的请求,定时重发。
(4)Forking
并行调用多个服务器,只要有一个成功便返回。5.4 集群的负载均衡
5.4.1 负载均衡配置方式
(2)客户端:
(3)方法级别:
dubbo:reference="..."
服务接口层(Service), 配置层(Config), 服务代理层(Proxy),服务注册层(Registry),集群层(Cluster),监控层(Monitor),远程调用层(Protocol)。信息交换层(Exchange),网络传输层(Transport),数据序列化层(Serialize)。8.2 配置文件
(1)设置配置属性和JavaBeanpublic class People {
private String name;
private int age;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("people", new PeopleBeanDefinitionParser());
}
public class PeopleBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
String age = element.getAttribute("age");
String id = element.getAttribute("id");
if (StringUtils.hasText(id)) {
builder.addPropertyValue("id", id);
}
if (StringUtils.hasText(name)) {
builder.addPropertyValue("name", name);
}
if (StringUtils.hasText(age)) {
builder.addPropertyValue("age", age);
}
}
@Override
protected Class> getBeanClass(Element element) {
return People.class;
}
}
spring.handlerhttp\://www.yihaomen.com/schema/people=com.dubbotest.xml.MyNamespaceHandler
http\://www.yihaomen.com/schema/people.xsd=META-INF/people.xsd
8.6 Dubbo 服务暴露的过程
(2)使用Dubbo 的SPI ,Javassist 动态代理生成服务类对象。8.8 集群容错和负载均衡
9. 一次RPC调用的过程
(2)消费者调用本地的helloService接口的sayHello方法。
(3)sayHello方法会被Java 动态代理拦截,生成一个Invoker实例,实例里面封装了协议类型(dubbo协议),确定数据结构:类名,方法名,参数类型,参数值,requestId, 超时时间 等等。
(4)序列化:把数据结构对象转化为二进制编码。hessian序列化。
(5)通信。 netty.
(6)服务提供端会反序列化请求,根据服务接口的配置,选择负载均衡策略,集群容错的策略。
(7)服务店的server,sayhello方法执行完成后,把requestId, 返回值序列化。发送给服务调用方。