说明
dubbo默认的protocol 是 DubboProtocol,默认的远程通信采用netty(不是netty4),默认的codec2是 hessian2。
这里我们来分析一下 dubbo实现 netty 异步转同步的细节。
# ExchangeChannel
上篇我们说过 dubbo 的上层通信封装在exchangeServer 和 exchangeClient。查看源码发现 发送消息的channel是ExchangeChannel,下面 查看HeaderExchangeChannel 的实现。
public ResponseFuture request(Object request, int timeout) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}
// create request.
Request req = new Request();
req.setVersion("2.0.0");
req.setTwoWay(true);
req.setData(request);
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
通过上面代码 我们发现 其实是生成了一个DefaultFuture结果。直接调用 future.get 可以同步等待结果。
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();
}
阻塞 主要利用
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
当有消息到来时候调用 done.signal()
具体来说,调用 exchangechannel的request方法 生成 Request 对象req,s这里 每
req有一唯一 的id。然后 DefaultFuture有以下 qin两个静态变量,保存了
reqId 到 DefaultFuture 的映射。
private static final Map<Long, Channel> CHANNELS = new ConcurrentHashMap<Long, Channel>();
private static final Map<Long, DefaultFuture> FUTURES = new ConcurrentHashMap<Long, DefaultFuture>();
当 netty 收到消息,会调用 com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#received 静态方法。
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());
}
}
因为 req和response 都有唯一 id对应起来,所以 很容易转成 同步。
调试 dubbo源码 demo使用multicast注册中心时,遇到provider端启动失败 报错如下:
java.net.ConnectException: 网络不可达 (connect failed)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at com.alibaba.dubbo.config.ServiceConfig.findConfigedHosts(ServiceConfig.java:577)
at com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol(ServiceConfig.java:469)
at com.alibaba.dubbo.config.ServiceConfig.doExportUrls(ServiceConfig.java:357)
at com.alibaba.dubbo.config.ServiceConfig.doExport(ServiceConfig.java:316)
at com.alibaba.dubbo.config.ServiceConfig.export(ServiceConfig.java:215)
at com.alibaba.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:121)
at com.alibaba.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:50)
调试 发现 findConfigedHosts hostToBind = InetAddress.getLocalHost().getHostAddress();这行 拿到 的hostToBind是 127.0.0.1 所以不能广播 。
参考文章
http://dubbo.apache.org/books/dubbo-user-book/references/registry/multicast.html
http://www.cnblogs.com/ghj1976/p/5276452.html
https://blog.youkuaiyun.com/raintungli/article/details/8191701
解决办法:
我用的 是Ubuntu系统 ,
sudo vi /etc/hosts
127.0.0.1 localhost
#127.0.1.1 tao-TM1604
192.168.3.3 tao-TM1604
注释 第二行 添加 第三行 192.168.3.是我局域网ip