在Dubbo中,同步调用的底层是通过DefaultFuture实现的,它使用了CompletableFuture(在较新版本中)或者自己实现的Future(旧版本)来等待结果。
当我们调用一个远程方法时,会生成一个Request对象,并分配一个请求ID,然后通过NettyChannel发送请求,同时创建一个DefaultFuture对象(与请求ID关联)来等待结果。
在NettyChannel中,发送消息是异步的,但是DefaultFuture会通过get方法同步等待结果。
大致代码流程(简化):
arduino
public class DefaultFuture implements ResponseFuture {
private final long id; // 请求ID
private final Channel channel;
private final Request request;
private final int timeout;
private volatile Response response;
// 创建DefaultFuture,并存储到全局Map中
public static DefaultFuture newFuture(Channel channel, Request request, int timeout) {
DefaultFuture future = new DefaultFuture(channel, request, timeout);
// 存储到Map,key为请求ID
FUTURES.put(request.getId(), future);
return future;
}
// 获取结果,会阻塞等待
public Object get(int timeout) throws RemotingException {
// 等待响应,直到超时
// 底层使用锁或CompletableFuture的get方法
}
}
当消费者收到响应时,会根据响应中的请求ID,从Map中找到对应的DefaultFuture,然后设置响应结果,并唤醒等待的线程。
所以,虽然网络通信是异步的,但通过DefaultFuture的同步等待,实现了同步调用的效果。
总结:Dubbo的同步服务是通过在消费者端使用Future模式(DefaultFuture)来阻塞等待服务提供者的响应结果来实现的。
Dubbo 的同步服务调用是其最核心和常用的调用模式。以下是 Dubbo 实现同步服务的详细机制和配置:
1. 同步调用基础原理
默认的同步调用
java
public class SyncServiceDemo {
@Reference
private UserService userService;
public void demo() {
// 默认就是同步调用 - 线程会阻塞直到返回结果
User user = userService.getUserById(123L);
System.out.println("用户信息: " + user);
// 等同于显式声明
User user2 = userService.getUserById(456L);
}
}
2. 底层同步实现机制
客户端同步调用流程
scala
public class DubboInvoker<T> extends AbstractInvoker<T> {
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
// 1. 构建请求
Request request = new Request();
request.setVersion(version);
request.setTwoWay(true); // 设置为双向通信
request.setData(invocation);
// 2. 发送请求并等待响应
DefaultFuture future = currentClient.request(request, timeout);
// 3. 同步等待结果
try {
Response response = future.get(timeout);
return new RpcResult(response.getResult());
} catch (InterruptedException e) {
throw new RpcException("Interrupted while waiting for response", e);
} catch (TimeoutException e) {
throw new RpcException("Timeout while waiting for response", e);
}
}
}
DefaultFuture 同步等待实现
java
public class DefaultFuture implements ResponseFuture {
private final long id;
private final Channel channel;
private final Request request;
private final int timeout;
private volatile Response response;
private final CountDownLatch latch = new CountDownLatch(1);
public static DefaultFuture newFuture(Channel channel, Request request, int timeout) {
DefaultFuture future = new DefaultFuture(channel, request, timeout);
// 存储到全局映射中
FUTURES.put(request.getId(), future);
TIMEOUT_CHECKER.addTimeoutTask(future);
return future;
}
// 同步获取结果
public Response get(int timeout) throws RemotingException {
if (timeout <= 0) {
timeout = Constants.DEFAULT_TIMEOUT;
}
if (!isDone()) {
long start = System.currentTimeMillis();
lock.lock();
try {
// 使用 Condition 进行等待
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(...);
}
}
return returnFromResponse();
}
// 收到响应时唤醒等待线程
public static void received(Response response) {
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
future.doReceived(response);
}
}
private void doReceived(Response res) {
lock.lock();
try {
response = res;
if (done != null) {
// 唤醒所有等待的线程
done.signalAll();
}
} finally {
lock.unlock();
}
}
}
3. 配置同步服务
XML 配置方式
xml
<!-- 服务提供者配置 -->
<dubbo:service interface="com.example.UserService"
ref="userService"
version="1.0.0"
timeout="3000"
retries="2"
actives="100" />
<!-- 服务消费者配置 -->
<dubbo:reference id="userService"
interface="com.example.UserService"
version="1.0.0"
timeout="5000"
retries="0"
check="false"
async="false" /> <!-- 明确设置为同步 -->
注解配置方式
kotlin
// 服务提供者
@Service(version = "1.0.0", timeout = 3000, retries = 2)
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Long id) {
// 模拟业务处理
return userRepository.findById(id);
}
}
// 服务消费者
@RestController
public class UserController {
@Reference(
version = "1.0.0",
timeout = 5000,
retries = 0,
async = false, // 同步调用
loadbalance = "roundrobin"
)
private UserService userService;
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
// 同步调用 - 阻塞直到返回结果
return userService.getUserById(id);
}
}
Spring Boot 配置
yaml
# application.yml
dubbo:
application:
name: user-service-consumer
registry:
address: zookeeper://127.0.0.1:2181
protocol:
name: dubbo
port: 20880
consumer:
timeout: 5000
check: false
retries: 0
async: false # 全局设置为同步
4. 同步调用的超时控制
多级别超时配置
less
public class TimeoutConfiguration {
// 1. 方法级别超时配置
@Reference(timeout = 3000, methods = {
@Method(name = "getUserById", timeout = 1000),
@Method(name = "updateUser", timeout = 5000)
})
private UserService userService;
// 2. 服务级别超时配置
@Reference(timeout = 5000)
private OrderService orderService;
}
// XML 配置方式
<dubbo:reference interface="com.example.UserService" timeout="3000">
<dubbo:method name="getUserById" timeout="1000" />
<dubbo:method name="updateUser" timeout="5000" />
</dubbo:reference>
超时异常处理
kotlin
public class SyncServiceWithTimeout {
@Reference(timeout = 2000)
private UserService userService;
public User getUserWithFallback(Long id) {
try {
return userService.getUserById(id);
} catch (RpcException e) {
if (e.isTimeout()) {
// 超时异常处理
logger.warn("获取用户信息超时,返回默认用户");
return getDefaultUser();
} else if (e.isNetwork()) {
// 网络异常处理
logger.error("网络异常", e);
throw new ServiceUnavailableException("服务暂时不可用");
} else {
// 其他异常