dubbo超時重試概述
consumer端和provider端都可以設置timeout。
超時優先級:consumer方法級>provider方法級>consumer接口級>provider接口級>consumer級>provider級
當consumer端調用超時,會觸發重試調用。
重試對應的配置屬性是retries。默認的重試次數是2。就是說,當調用超時,會最多重試2次,如果仍然失敗,會提示異常。
對於查詢或刪除來說,接口重試是冪等的。
對於新增數據,如果retries>0,則要做冪等處理,否則會造成重復數據入庫而產生bug。安全起見,可單獨設置retries=0。
【說明】在直連的情況下,是不會觸發重試的。
代碼
§1. 服務接口定義:
package dubbodemo.contract;
public interface HelloWord { String say(); String add(String str); }
§2. 服務提供方
Provider application.yml dubbo配置timeout和retries:

dubbo: application: name: zhanggz-dubbodemo registry: # address: N/A address: zookeeper://127.0.0.1:2181 protocol: port: 28088 name: dubbo scan: base-packages: dubbodemo.provider provider: timeout: 2200 retries: 3
Provider代碼:

package dubbodemo.provider; import dubbodemo.contract.HelloWord; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomUtils; import org.apache.dubbo.config.annotation.Service; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @Slf4j @Service(retries = 3) @SpringBootApplication public class ProviderApplication implements HelloWord { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); } public String add(String str) { log.info("add入參:{}", str); long start = System.currentTimeMillis(); try { //int i=1/0; try { int r = RandomUtils.nextInt(1000, 2000); log.info("sleep time={}", r); Thread.sleep(r); } catch (InterruptedException e) { e.printStackTrace(); } return "test retry:" + str; } finally { log.info("duration={}", System.currentTimeMillis() - start); } } public String say() { return ("hello"); } }
§3. 服務消費方
Consumer application.yml dubbo配置timeout和retries:

dubbo: application: name: zhanggz-dubbodemo-consumer registry: address: zookeeper://127.0.0.1:2181 # address: N/A protocol: name: dubbo consumer: timeout: 1000 # retries: 0
Consumer代碼:

package dubbodemo.consumer; import dubbodemo.contract.HelloWord; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.Reference; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class DubboConsumerTest { @Reference(/*url = "dubbo://localhost:28088",*/) private HelloWord helloWord; @Test public void testAddRetry() { long start = System.currentTimeMillis(); log.info("請求開始..."); try { String str = helloWord.add("str"); log.info("返回值={}", str); } finally { log.info("duration=={}", System.currentTimeMillis() - start); } } }
測試結論
--------直連的情況下,測試dubbo調用超時。
將Provider方法利用Thread.sleep(2000)來模擬超時。
consumer不設置timeout(默認值=1000ms),改變provider的timeout值
§1.【provider】設置provider.timeout=1000 ,因為方法執行耗時是2000ms,會有WARN提示。
2020-07-23 18:53:57.427 INFO 13356 --- [:20880-thread-2] dubbodemo.provider.ProviderApplication : duration=2000 2020-07-23 18:53:57.432 WARN 13356 --- [:20880-thread-2] o.apache.dubbo.rpc.filter.TimeoutFilter : [DUBBO] invoke time out.
method: add arguments: [str] ,
url is dubbo://192.168.40.69:20880/dubbodemo.contract.HelloWord?anyhost=true&application=zhanggz-dubbodemo&bean.name=ServiceBean:dubbodemo.contract.HelloWord&bind.ip=192.168.40.69&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=dubbodemo.contract.HelloWord&methods=add,say&pid=13356&qos.enable=false®ister=true&release=2.7.3&side=provider&timeout=1000×tamp=1595501612605,
invoke elapsed 2005 ms., dubbo version: 2.7.3, current host: 192.168.40.69
§2. 【provider】設置provider.timeout=2200, 方法耗時2000<2200,不會提示invoke timeout。
2020-07-23 18:57:16.949 INFO 12280 --- [:20880-thread-2] dubbodemo.provider.ProviderApplication : duration=2000
§3. 不管Provider如何設置timeout,因為Consumer的timeout=1000ms,所以consumer都會收到timeout異常。
2020-07-23 18:53:56.273 INFO 13216 --- [ main] dubbodemo.consumer.DubboConsumerTest : duration==1040 org.apache.dubbo.rpc.RpcException: Invoke remote method timeout.
method: add,
provider: dubbo://localhost:20880/dubbodemo.contract.HelloWord?application=zhanggz-dubbodemo-consumer&interface=dubbodemo.contract.HelloWord&lazy=false&pid=13216&qos.enable=false®ister.ip=192.168.40.69&remote.application=&side=consumer&sticky=false,
cause: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2020-07-23 18:53:55.250, end time: 2020-07-23 18:53:56.272, client elapsed: 106 ms, server elapsed: 916 ms, timeout: 1000 ms,
request: Request [id=0, version=2.0.2, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=add, parameterTypes=[class java.lang.String], arguments=[str], attachments={path=dubbodemo.contract.HelloWord, interface=dubbodemo.contract.HelloWord, version=0.0.0}]],
channel: /192.168.40.69:1813 -> /192.168.40.69:20880
§1. Provider服務停止后,consumer端無法啟動
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dubbodemo.consumer.DubboConsumerTest':
Injection of @Reference dependencies is failed;
nested exception is org.apache.dubbo.rpc.RpcException: Fail to create remoting client for service(
dubbo://localhost:20880/dubbodemo.contract.HelloWord?application=zhanggz-dubbodemo-consumer&codec=dubbo&heartbeat=60000&interface=dubbodemo.contract.HelloWord&lazy=false&pid=5236&qos.enable=false®ister.ip=192.168.40.69&remote.application=&side=consumer&sticky=false&timeout=2200
): client(url:
dubbo://localhost:20880/dubbodemo.contract.HelloWord?application=zhanggz-dubbodemo-consumer&codec=dubbo&heartbeat=60000&interface=dubbodemo.contract.HelloWord&lazy=false&pid=5236&qos.enable=false®ister.ip=192.168.40.69&remote.application=&side=consumer&sticky=false&timeout=2200
) failed to connect to server localhost/127.0.0.1:20880, error message is:Connection refused: no further information: /192.168.40.69:20880
§2. Provider服務停止后,consumer端設置dubbo.consumer.check=false。則可以啟動consumer服務。
check屬性說明:Check if service provider exists, if not exists, it will be fast fail
不過,當調用遠程服務的時候,由於遠程服務不可用,所以當然會報錯。
- 錯誤提示(連接了注冊中心的場景):
org.apache.dubbo.rpc.RpcException: No provider available from registry 192.168.40.84:2181
for service dubbodemo.contract.HelloWord on consumer 192.168.40.69 use dubbo version 2.7.3,
please check status of providers(disabled, not registered or in blacklist).
- 錯誤提示(直連的場景):
org.apache.dubbo.rpc.RpcException: Failed to invoke remote method: add,
provider: dubbo://localhost:20880/dubbodemo.contract.HelloWord?application=zhanggz-dubbodemo-consumer&check=false&interface=dubbodemo.contract.HelloWord&lazy=false&pid=12504&qos.enable=false®ister.ip=192.168.40.69&remote.application=&side=consumer&sticky=false&timeout=2200,
cause: message can not send, because channel is closed .
url:dubbo://localhost:20880/dubbodemo.contract.HelloWord?application=zhanggz-dubbodemo-consumer&check=false&codec=dubbo&heartbeat=60000&interface=dubbodemo.contract.HelloWord&lazy=false&pid=12504&qos.enable=false®ister.ip=192.168.40.69&remote.application=&side=consumer&sticky=false&timeout=2200
----------------------------------
consumer端,設置retries=3
2020-07-24 17:12:57.389 INFO 7468 --- [ main] dubbodemo.consumer.DubboConsumerTest : 請求開始... 2020-07-24 17:13:00.202 INFO 7468 --- [ main] dubbodemo.consumer.DubboConsumerTest : 返回值=test retry:str 2020-07-24 17:13:00.202 INFO 7468 --- [ main] dubbodemo.consumer.DubboConsumerTest : duration==2813
provider端:最多會被執行1+retries=4次
2020-07-24 17:12:57.540 INFO 2372 --- [28088-thread-17] dubbodemo.provider.ProviderApplication : add入參:str 2020-07-24 17:12:57.541 INFO 2372 --- [28088-thread-17] dubbodemo.provider.ProviderApplication : r=1169 2020-07-24 17:12:58.444 INFO 2372 --- [28088-thread-18] dubbodemo.provider.ProviderApplication : add入參:str 2020-07-24 17:12:58.444 INFO 2372 --- [28088-thread-18] dubbodemo.provider.ProviderApplication : r=1934 2020-07-24 17:12:58.711 INFO 2372 --- [28088-thread-17] dubbodemo.provider.ProviderApplication : duration=1170 2020-07-24 17:12:59.461 INFO 2372 --- [28088-thread-19] dubbodemo.provider.ProviderApplication : add入參:str 2020-07-24 17:12:59.461 INFO 2372 --- [28088-thread-19] dubbodemo.provider.ProviderApplication : r=736 2020-07-24 17:13:00.198 INFO 2372 --- [28088-thread-19] dubbodemo.provider.ProviderApplication : duration=737 2020-07-24 17:13:00.379 INFO 2372 --- [28088-thread-18] dubbodemo.provider.ProviderApplication : duration=1935
優先級測試
§1. Provider設置retries=1 consumer設置retries=3,服務端會重試1次。
注意,log里的Tried 2 times 指的是一共調用了2次。即首次+重試的那1次。
org.apache.dubbo.rpc.RpcException: Failed to invoke the method add in the service dubbodemo.contract.HelloWord.
Tried 2 times of the providers [192.168.40.84:28088, 192.168.40.69:28088] (2/2) from the registry 192.168.40.84:2181 on the consumer 192.168.40.69 using the dubbo version 2.7.3.
Last error is: Invoke remote method timeout. method: add,
provider: dubbo://192.168.40.84:28088/dubbodemo.contract.HelloWord?anyhost=true&application=zhanggz-dubbodemo-consumer&bean.name=ServiceBean:dubbodemo.contract.HelloWord&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=dubbodemo.contract.HelloWord&lazy=false&methods=add,say&pid=23324&qos.enable=false®ister=true®ister.ip=192.168.40.69&release=2.7.3&remote.application=zhanggz-dubbodemo&retries=1&revision=1.0-SNAPSHOT&side=consumer&sticky=false&timeout=1000×tamp=1596179853357,
cause: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2020-07-31 15:33:52.809, end time: 2020-07-31 15:33:53.826, client elapsed: 1 ms, server elapsed: 1016 ms, timeout: 1000 ms,
request: Request [id=1, version=2.0.2, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=add, parameterTypes=[class java.lang.String], arguments=[str], attachments={path=dubbodemo.contract.HelloWord, interface=dubbodemo.contract.HelloWord, version=0.0.0, timeout=1000}]],
channel: /192.168.40.69:3666 -> /192.168.40.84:28088
§2. Provider設置retries=0 consumer設置retries=3,服務端不會重試。
org.apache.dubbo.rpc.RpcException: Failed to invoke the method add in the service dubbodemo.contract.HelloWord.
Tried 1 times of the providers [192.168.40.69:28088] (1/1) from the registry 127.0.0.1:2181 on the consumer 192.168.40.69 using the dubbo version 2.7.3.
Last error is: Invoke remote method timeout. method: add,
provider: dubbo://192.168.40.69:28088/dubbodemo.contract.HelloWord?anyhost=true&application=zhanggz-dubbodemo-consumer&bean.name=ServiceBean:dubbodemo.contract.HelloWord&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=dubbodemo.contract.HelloWord&lazy=false&methods=add,say&pid=9436&qos.enable=false®ister=true®ister.ip=192.168.40.69&release=2.7.3&remote.application=zhanggz-dubbodemo&retries=0&side=consumer&sticky=false&timeout=1000×tamp=1596170589487,
cause: org.apache.dubbo.remoting.TimeoutException: Waiting server-side response timeout by scan timer. start time: 2020-07-31 12:43:36.395, end time: 2020-07-31 12:43:37.419, client elapsed: 111 ms, server elapsed: 912 ms, timeout: 1000 ms,
request: Request [id=0, version=2.0.2, twoway=true, event=false, broken=false, data=RpcInvocation [methodName=add, parameterTypes=[class java.lang.String], arguments=[str], attachments={path=dubbodemo.contract.HelloWord, interface=dubbodemo.contract.HelloWord, version=0.0.0, timeout=1000}]],
channel: /192.168.40.69:2295 -> /192.168.40.69:28088