dubbo探究

一 占位

待整理。。 

二 问题汇总

1 谈谈dubbo的超时重试

dubbo 启动时默认有重试机制和超时机制。如果在一定的时间内,provider没有返回,则认为本次调用失败。重试机制出现在调用失败时,会再次调用,如果在配置的调用次数内都失败,则认为此次请求异常,消费端出现RpcException提示retry了多少次还是失败。

如果出现超时,通常是业务处理太慢,可在服务提供方执行 jstack PID > jstack.log 分析线程都卡在哪个方法调用上。如果不能调优性能,请将timeout设大。

dubbo消费端设置的超时时间需要根据业务实际情况来设定,如果设置的过短,一些复杂业务需要很长时间完成,导致在设定的超时时间内无法完成正常的业务处理。这样消费端达到超时时间,那么dubbo会进行重试,不合理的重试在一些特殊的业务场景下可能会引发很多问题。比如发送邮件,可能会发出多份重复邮件等。

dubbo调用服务不成功时,默认会重试两次。dubbo的路由机制,会把超时的请求路由到其他机器上,而不是本机尝试,所以dubbo的重试机制也能得到一定程度的保证。但是不合理地配置重试次数,当失败时会进行重试多次,这样在某个时间点出现性能问题,调用方继续重试请求为正常retries倍,容易引起服务雪崩。

最佳实践:

1.对于核心的服务中心,去除dubbo超时重试机制,并重新评估设置超时时间。
2.业务处理代码必须放在服务端,客户端只做参数验证和服务调用,不涉及业务流程处理

2 dubbo的provider和consumer都配置timeout超时时间, 以哪个为准

在dubbo的provider和consumer的配置文件中,如果都配置了timeout的超时时间,dubbo默认以consumer中配置的时间为准

provider.xml的配置:

<dubbo:service timeout="4000" retries="0" interface="com.dingding.tms.bms.service.BillingZfbCodOrderService" ref="billingZfbCodOrderService" registry="globalRegistry"/>

conusmer中的配置:

<dubbo:reference id="billingInterService" interface="com.dingding.tms.bms.service.BillingInterService" protocol="dubbo" check="false" registry="globalRegistry" timeout="3000"/>

最后这个service在调用时的超时时间就是3秒

另外,超过3s后的表现如下:

(1) consumer会在超过3秒时得到一个调用超时的异常。

(2) provider中代码的执行不会因为超时而中断,在执行完毕后,会得到一个dubbo的警告。

3 关于dubbo的配置上,你有什么经验

(1) 在Provider上尽量多地配置Consumer的属性

原因: 

a. 作为服务的提供者,比服务使用方更清楚服务性能参数,如调用的超时时间,合理的重试次数等

b. 在provider配置后,consumer不配置则自动使用provider的配置,即作为了consumer端的缺省值。否则,consumer会使用consumer端的全局配置,这对于provider端往往是不可控的。

ps: 配置的优先级:

1. 方法级配置别优于接口级别,即小Scope优先 
2. Consumer端配置优于Provider配置,优于全局配置 
3. Dubbo Hard Code的配置值(默认)

根据规则2, 纵使消费端配置优于服务器配置,但消费端配置超时时间不能随心所欲,需要根据业务实际情况来设定。如果设置的太短,复杂业务本来就需要很长时间完成,服务端无法在设定的超时时间内完成业务处理; 如果设置太长,会由于服务端或者网络问题导致客户端大量线程挂起。

配置示例

<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService"
    timeout="300" retry="2" loadbalance="random" actives="0"/>
 
<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端可以配置的consumer属性有:

1 timeout 方法调用超时时间

2 retry 失败重试次数,缺省是2(加上第一次调用,共调用三次)

3 loadbalance 负责均衡算法(即多个provider如何挑选provider调用),缺省是随机(random),还可以有轮训(roundrobin),最不活跃优先(leastactive, 指从consumer端并发调用效果最好的provider,这样可以相对减少并发的堆积)

4 actives 消费端最大并发调用限制,即当consumer对一个服务的并发调用达到上限后,新调用会wait直到超时。粒度上,在方法上配置(dubbo: method)则并发针对方法,在接口上配置(dubbo:service)则并发限制针对服务

(2) 在Provider上配置合理的provider属性

<dubbo:protocol threads="200" />
<dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" executes="200" >
    <dubbo:method name="findAllPerson" executes="50" />
</dubbo:service>

Provider上可以配置的Provider端属性有:

  1. threads,服务线程池大小
  2. executes,一个服务提供者并行执行请求上限,即当Provider对一个服务的并发调用到上限后,新调用会wait(Consumer可能到超时)。在方法上配置(dubbo:method )则并发限制针对方法,在接口上配置(dubbo:service),则并发限制针对服务。

超时配置:

Dubbo消费端 
全局超时配置

<dubbo:consumer timeout="5000" />

指定接口以及特定方法超时配置

<dubbo:reference interface="com.foo.BarService" timeout="2000">
    <dubbo:method name="sayHello" timeout="3000" />
</dubbo:reference>

Dubbo服务端 
全局超时配置

<dubbo:provider timeout="5000" />

指定接口以及特定方法超时配置

<dubbo:provider interface="com.foo.BarService" timeout="2000">
    <dubbo:method name="sayHello" timeout="3000" />
</dubbo:provider>

4 dubbo协议超时实现

 Dubbo协议超时实现使用了Future模式,主要涉及类DubboInvoker,ResponseFuture, DefaultFuture。 
ResponseFuture.get()在请求还未处理完或未到超时前一直是wait状态;响应达到后,设置请求状态,并进行notify唤醒。

public Object get() throws RemotingException {
        return get(timeout);
    }

    public Object get(int timeout) throws RemotingException {
        if (timeout <= 0) {
            timeout = Constants.DEFAULT_TIMEOUT;
        }
        if (! isDone()) {
            long start = System.currentTimeMillis();
            lock.lock();
            try {
                while (! isDone()) {
                    done.await(timeout, TimeUnit.MILLISECONDS);
                    if (isDone() || System.currentTimeMillis() - start > timeout) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                lock.unlock();
            }
            if (! isDone()) {
                throw new TimeoutException(sent > 0, channel, getTimeoutMessage(false));
            }
        }
        return returnFromResponse();
    }
View Code
    public static void received(Channel channel, Response response) {
        try {
            DefaultFuture future = FUTURES.remove(response.getId());
            if (future != null) {
                future.doReceived(response);
            } else {
                logger.warn("The timeout response finally returned at " 
                            + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())) 
                            + ", response " + response 
                            + (channel == null ? "" : ", channel: " + channel.getLocalAddress() 
                                + " -> " + channel.getRemoteAddress()));
            }
        } finally {
            CHANNELS.remove(response.getId());
        }
    }

    private void doReceived(Response res) {
        lock.lock();
        try {
            response = res;
            if (done != null) {
                done.signal();
            }
        } finally {
            lock.unlock();
        }
        if (callback != null) {
            invokeCallback(callback);
        }
    }
View Code

@see http://shiyanjun.cn/archives/325.html dubbo架构设计详解

原文地址:https://www.cnblogs.com/balfish/p/8663143.html