说一说提供者启动流程?
ServiceAnnotationBeanPostProcessor
实现了BeanDefinitionRegistryPostProcessor
接口,在它的registerServiceBean方法中,会为带有@Service注解的类注册两个BeanDefine信息: 原始类对应的BeanDefine(类名首字母小写)
ServiceBean对应的BeanDefine(ServiceBean:com.sxy.sdubbo.service.ILearn)
. 所以,当我们使用了Dubbo的@Service注解之后,其实没必要再使用Spring的相关注解了. 还有一点需要注意的是ServiceBean里面持有原始Bean的name
ServiceBean
的舞台,ServiceBean
实现了InitializingBean
接口,在它的afterPropertiesSet
方法中会完成所有和提供者相关的工作. 流程如下:
2.1 如果有,则设置相关属性: ProviderConfig ApplicationConfig ModuleConfig RegistryConfig MetadataReportConfig ConfigCenterConfig MonitorConfig MetricsConfig ProtocolConfig
2.2 前置校验,在这个步骤中,如果有必要会启动配置中心
2.3 找到所有的注册中心(n)和需要暴露的协议(m),即一个提供者需要暴露的服务数为n*m
2.4 构造参数到Map中: application metrics module provider version methods 等
2.5 根据scope属性选择本地暴露还是远程暴露,默认情况下先暴露本地服务,然后暴露远程服务
2.6 利用Javassist
动态生成一个代理对象Wrapper
,该代理对象持有原始实现类的引用
2.7 生成一个AbstractProxyInvoker
对象,在它的doInvoke
方法中,调用了代理对象Wrapper
的invokeMethod
方法
2.8 服务暴露,默认情况下,网络通信通过Netty4,即开启一个NettyServer. 这部分和DubboProtocol
相关
2.9 注册服务到注册中心,这部分和RegistryProtocol
相关. 以ZK为例,注册服务到providers
节点,同时监听configurators
节点
2.10 最终返回一个Exporter
对象,该对象内部持有Invoker
引用.可以简单的认为Exporter
对象即代表最终暴露的那个服务,在服务有变化的时候,比如动态配置变了,需要重新刷新Exporter
对象ServiceBeanExportedEvent事件
上面的流程记不住是吧?那来个简单版的:
ServiceBean#afterPropertiesSet
方法Wrapper
,然后再创建一个AbstractProxyInvoker
,在它的doInvoke
方法中调用了Wrapper#invokeMethod
方法DubboProtocol
相关Exporter
对象,该对象内部持有Invoker
引用,即关系如下: Exporter => Invoker =>Wrapper(代理对象) => 原始类方法
说一说提消费者启动流程?
ReferenceAnnotationBeanPostProcessor
实现了InstantiationAwareBeanPostProcessorAdapter
接口,所以它会在Bean实例化前后执行. 里面涉及到一个doGetInjectedBean
方法,根据名字也可以猜测到这里就是要注入我们依赖的Bean, 主要就是处理@Reference
@Reference
的时候,涉及到这么几个对象:
2.1 doGetInjectedBean
方法返回的是一个代理对象(JDK动态代理),假如它是proxy1
2.2 ReferenceBean
: 是一个FactoryBean
,但是它的getObject
返回的是一个代理对象,假如它是proxy2
2.3 在创建proxy1
的时候,需要提供一个InvocationHandler,这里是ReferenceBeanInvocationHandler
, 在创建ReferenceBeanInvocationHandler
的时候需要依赖proxy2
2.4 所以,最终注入的是proxy1
,但是在调用的时候是:proxy1 => ReferenceBeanInvocationHandler => proxy2
ReferenceBean
中,流程如下:
3.1 前置校验,有必要就启动注册中心
3.2 构造参数到Map中: application metrics module interfaceName consumer methods 等
3.3 创建Invoker
. 如果是单注册中心直接返回一个Invoker
; 如果是多注册中心则为每个注册中心创建一个Invoker
,然后将将这些Invokers
合并成一个
3.4 Invoker
其实是基于监听providers
节点来创建的.先注册到consumers
节点,然后监听providers routers configurators
,监听器中拿到providerUrls
后,根据为每条url生成一个Invoker
对象. 需要注意的是,在生成Invoker
时,涉及到NettyClient与NettyServer建立连接
3.5 基于步骤3.3创建的Invoker
,使用Javassist
创建代理对象并返回(proxy2
)
3.6 所以最后的调用关系是: proxy2 => Invoker => 网络通信
说一说消费者发送请求到接收响应整个流程?
为什么呢?
你得先把那两个代理对象引出来,才好继续往下说,要不然无从下手p0
2.2 提供者代理对象: p1
消费端发送请求
p0
执行对应方法,然后调用Invoker
的对应方法Invoker
requestId
. 返回的是一个DefaultFuture
对象,并且有一个Map缓存所有请求ID和DefaultFuture的关系服务端响应请求
xxHandler
处理,请求来到DubboProtocol.requestHandler
,然后执行: Exporter => Invoker => p1 => 原始对象
Response
对象,它的ID属性值就是Request
对象的requestId
值,这样请求和响应就关联起来了(实际者一块没这么简单,涉及到回调处理,这里就不详细说了)Response
对象发送给消费端消费端响应结果
CompletableFuture#complete
方法,这样就可以让执行了CompletableFuture#get
的用户线程得到响应,获取结果返回说一说路由规则在前还是负载均衡在前?
路由在前
Invoker
,消费端监听了routers
节点,所以在路由规则发生变化的时候,就会刷新Invoker列表
Invoker列表
中选出一个Invoker
,这时候的Invoker列表
其实是经过路由筛选过的Invokers
. 为了避免大多数流量都请求到同一台机器或部分机器没有流量,需要根据一种负载算法选择一个Invoker
说一说Dubbo的扩展机制?
ExtensionFactory
实现. 支持两种属性的自动注入:
2.1: SPI类型的属性. 这个是基于SpiExtensionFactory
实现
2.2: Spring里面的Bean. 这个是基于SpringExtensionFactory
实现,里面直接通过ApplicationContext获取BeanWrapper机制
实现,即包一层. 这种Wrapper类
有一个特点,构造函数需要传入被包装实例
,如下public ProtocolFilterWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
说一说Filter机制?
Filter
是一个SPI接口Filter
都构建成一条Filter链
.消费端对应一条Filter链
,提供端对应一条Filter链
Filter链
,核心类为ProtocolFilterWrapper
,它实现了Protocol
接口. 从名字可以看出这是一个对Protocol
的AOP处理,处理逻辑就是构造Filter链
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 获取消费端 或 提供端 的所有 Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
......
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult;
}
......
};
}
}
return new CallbackRegistrationInvoker<>(last, filters);
}
说一说优雅停机?
DubboShutdownHook
主要分两个步骤:
10s
Protocol
2.1 会向消费端发送READ_ONLY
事件. 消费者接受之后主动排除这个节点,将请求发往其他正常节点
2.2 关闭业务线程池,这个过程将会尽可能将线程池中的任务执行完毕,再关闭线程池
2.3 关闭NettyServer
2.4 关闭NettyClient说一说回声测试?
EchoService
接口,只所以需要将任意服务引用强制转换为EchoService
即可使用EchoFilter
中进行拦截,判断是否是回声测试方法,如果是则直接返回入参; 如果不是则发起正常调用// 回声测试可用性
EchoService echoService = (EchoService) memberService; // 强制转型为EchoService
String status = echoService.$echo("OK");
@Activate(group = CommonConstants.PROVIDER, order = -110000)
public class EchoFilter implements Filter {
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
if (inv.getMethodName().equals($ECHO) && inv.getArguments() != null && inv.getArguments().length == 1) {
return AsyncRpcResult.newDefaultAsyncResult(inv.getArguments()[0], inv);
}
return invoker.invoke(inv);
}
}
说一说服务降级?
Mock
机制,Mock
使用方式有两种:
2.1 提供端直接返回一个固定的字符串
2.2 在接口服务xxService的目录下创建相应的mock业务处理类接口名+Mock后缀
,同时实现业务接口
xxService()MockInvoker#invoke
方法说一说本地存根?
p0
,以p0
为入参,创建一个Stub
对象,然后暴露给用户(最终的p0
)public class DemoServiceStub implements DemoService {
// 这里的 demoService 代表的是消费端的代理对象p0
private final DemoService demoService;
public DemoServiceStub(DemoService demoService) {
this.demoService = demoService;
}
public String sayHello(String name) {
System.out.println("进入DemoServiceStub");
return demoService.sayHello(name);
}
}
说一说泛化调用?
GenericService
GenericImplFilter
实现GenericFilter
实现