dubbo备忘录
1.负载均衡策略
Random LoadBalance :随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance :轮询,按公约后的权重设置轮询比率。 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance :最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance :一致性 Hash,相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />
2.集群容错策略
Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。
Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
3.dubbo停机
1.服务提供方
停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
2.服务消费方
停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
这里还有两件事,取消注册,以及取消订阅
4.dubbo启动
5.dubbo注册ip
DUBBO启动时通过ServiceConfig 的 findConfigedHosts寻找本机地址
首先是判断是否有配置host ip,通过环境变量DUBBO_IP_TO_REGISTRY查找,第二优先级是通过配置中的dubbo.protocol.host来获取本机ip,并注册
如果没有配置,那么通过本机的hostname来查找本机的ip;
/**
* Register & bind IP address for service provider, can be configured separately.
* Configuration priority: environment variables -> java system properties -> host property in config file ->
* /etc/hosts -> default network address -> first available network address
*
* @param protocolConfig
* @param registryURLs
* @param map
* @return
*/
private String findConfigedHosts(ProtocolConfig protocolConfig, List<URL> registryURLs, Map<String, String> map) {
boolean anyhost = false;
String hostToBind = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_BIND);
// if bind ip is not found in environment, keep looking up
if (StringUtils.isEmpty(hostToBind)) {
hostToBind = protocolConfig.getHost();
if (provider != null && StringUtils.isEmpty(hostToBind)) {
hostToBind = provider.getHost();
}
if (StringUtils.isEmpty(hostToBind)) {
anyhost = true;
hostToBind = getLocalHost();
if (StringUtils.isEmpty(hostToBind)) {
hostToBind = findHostToBindByConnectRegistries(registryURLs);
}
}
}
map.put(Constants.BIND_IP_KEY, hostToBind);
// registry ip is not used for bind ip by default
String hostToRegistry = getValueFromConfig(protocolConfig, Constants.DUBBO_IP_TO_REGISTRY);
if (StringUtils.isEmpty(hostToRegistry)) {
// bind ip is used as registry ip by default
hostToRegistry = hostToBind;
}
map.put(Constants.ANYHOST_KEY, String.valueOf(anyhost));
return hostToRegistry;
}
6.服务端连接池
系统默认使用FixedThreadPoll,连接池大小默认200,队列大小默认为0;
7.消费端Reference初始化
消费端会通过@Reference来注入provider引用,对应的就是dubbo的ReferenceBean,实现了spring的FactoryBean(FactoryBean 通常是用来创建比较复杂的bean);
注入reference的入口是通过ReferenceAnnotationBeanPostProcessor
初始化的
8.dubbo调用链
//消费端发送请求
InvokerInvocationHandler.invoke
MockClusterInvoker.invoke
AbstractInvoker.invoke
DubboInvoker.doInvoke(final Invocation invocation)
ReferenceCountExchangeClient.request(Object request, int timeout)
HeaderExchangeClient.request(Object request, int timeout)
HeaderExchangeChannel.request(Object request, int timeout)
AbstractPeer.send(Object message)
NettyChannel.send(Object message, boolean sent)
AbstractChannel.writeAndFlush(Object msg)
//服务端接收请求
netty
AllChannelHandler.received(Channel channel, Object message)
asyn-----------------------------------------------------
ChannelEventRunnable.run()
DecodeHandler.received(Channel channel, Object message)
HeaderExchangeHandler.received(Channel channel, Object message)
DubboProtocol.reply(ExchangeChannel channel, Object message)
CallbackRegistrationInvoker.invoke(Invocation invocation)
ProtocolFilterWrapper$1.invoke(next, invocation)
.
.
.
AbstractProxyInvoker.invoke(Invocation invocation)
HeaderExchangeChannel.send(response)
//消费端接受响应
netty
AllChannelHandler.received(Channel channel, Object message)
asyn-----------------------------------------------------
ChannelEventRunnable.run()
DecodeHandler.received(Channel channel, Object message)
HeaderExchangeHandler.received(Channel channel, Object message)
HeaderExchangeHandler.handleResponse(channel, (Response) message)
DefaultFuture.received(channel, response) 获取到response对应的DefaultFuture,并将response写入DefaultFuture,将DefaultFuture置为completed
9.处理请求(处理响应)的线程池创建链路
//DubboProtocol.java
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service
//export an stub service for dispatching event
//创建ExchangeServer
openServer(url);
optimizeSerialization(url);
return exporter;
}
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
//如果第一次,那么需要创建ExchangeServer
if (server == null) {
serverMap.put(key, createServer(url));
}
}
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
//WrappedChannelHandler.java
public WrappedChannelHandler(ChannelHandler handler, URL url) {
this.handler = handler;
this.url = url;
//在这里根据url上的配置,去创建指定thread数量,指定队列数量的线程池
executor = (ExecutorService) ExtensionLoader.getExtensionLoader(ThreadPool.class).getAdaptiveExtension().getExecutor(url);
String componentKey = Constants.EXECUTOR_SERVICE_COMPONENT_KEY;
if (CONSUMER_SIDE.equalsIgnoreCase(url.getParameter(SIDE_KEY))) {
componentKey = CONSUMER_SIDE;
}
DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
dataStore.put(componentKey, Integer.toString(url.getPort()), executor);
}
posted on 2019-09-19 17:11 mindSucker 阅读(199) 评论(0) 编辑 收藏 举报