consumer发送请求,接收响应
一般情况,consumer发送请求时,创建一个DefaultFuture对象,然后阻塞并等待响应。DefaultFuture类,封装了请求和响应:
// 省略其他代码
public class DefaultFuture implements ResponseFuture {
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
//waiting map
private static final Map<Long, DefaultFuture> FUTURES
= new ConcurrentHashMap<Long, DefaultFuture>();
private final Request request;
private volatile Response response;
}
1. consumer发送请求,阻塞等待响应。

调用代码:
HelloService helloService = (HelloService) appCtx.getBean("hello");
String hello = helloService.sayHello();
调用代理对象的sayHello方法,代理类proxy0由javassist动态生成,代码大致如下:
class com.alibaba.dubbo.common.bytecode.proxy0 implements
com.alibaba.dubbo.rpc.service.EchoService, com.zhang.HelloService {
public <init>(java.lang.reflect.InvocationHandler arg0){
handler=$1;
}
public static java.lang.reflect.Method[] methods;
private java.lang.reflect.InvocationHandler handler; public java.lang.String sayHello(){
Object[] args = new Object[0];
//handler是InvokerInvocationHandler对象
Object ret = handler.invoke(this, methods[0], args);
return (java.lang.String)ret;
}
public java.lang.Object $echo(java.lang.Object arg0){
Object[] args = new Object[1];
args[0] = ($w)$1;
Object ret = handler.invoke(this, methods[1], args);
return (java.lang.Object)ret;
}
}
进InvokerInvocationHandler
//InvokerInvocationHandler类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
//invoker是MockClusterInvoker
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
跳过中间步骤,进到DubboInvoker的doInvoke方法,只分析一般的阻塞调用
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
//currentClient对象是 ReferenceCountExchangeClient/HeaderExchangeClient/HeaderExchangeChannel
//拆分来看:ResponseFuture responseFuture = currentClient.request(inv, timeout);
// responseFuture.get(); 调用get导致调用线程阻塞
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION,
"Invoke remote method timeout. method: " + invocation.getMethodName() +
", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " +
invocation.getMethodName() + ", provider: " + getUrl() +
", cause: " + e.getMessage(), e);
}
}
进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
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()) {
//调用线程等待直到超时,或者被DubboClientHandler线程唤醒
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 Object returnFromResponse() throws RemotingException {
Response res = response;
if (res == null) {
throw new IllegalStateException("response cannot be null");
}
if (res.getStatus() == Response.OK) {
return res.getResult();
}
if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT,
channel, res.getErrorMessage());
}
throw new RemotingException(channel, res.getErrorMessage());
}
2. 在DubboClientHandler线程中,consumer接收响应,唤醒调用线程。

从ChannelEventRunnable进
//ChannelEventRunnable类
public void run() {
//省略其他代码
switch (state) {
case RECEIVED:
handler.received(channel, message);break;
default:
logger.warn("unknown state: " + state + ", message is " + message);
}
}
进DecodeHandler
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Decodeable) {
decode(message);
}
if (message instanceof Request) {
decode(((Request)message).getData());
}
if (message instanceof Response) {
decode( ((Response)message).getResult());
}
handler.received(channel, message);
}
进HeaderExchangeHandler
public void received(Channel channel, Object message) throws RemotingException {
channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
try {
if (message instanceof Request) {
// handle request.
Request request = (Request) message;
if (request.isEvent()) {
handlerEvent(channel, request);
} else {
if (request.isTwoWay()) {
Response response = handleRequest(exchangeChannel, request);
channel.send(response);
} else {
handler.received(exchangeChannel, request.getData());
}
}
} else if (message instanceof Response) {
handleResponse(channel, (Response) message);
} else if (message instanceof String) {
if (isClientSide(channel)) {
Exception e = new Exception("Dubbo client can not supported string message: "
+ message + " in channel: " + channel + ", url: " + channel.getUrl());
logger.error(e.getMessage(), e);
} else {
String echo = handler.telnet(channel, (String) message);
if (echo != null && echo.length() > 0) {
channel.send(echo);
}
}
} else {
handler.received(exchangeChannel, message);
}
} finally {
HeaderExchangeChannel.removeChannelIfDisconnected(channel);
}
}
static void handleResponse(Channel channel, Response response) throws RemotingException {
if (response != null && !response.isHeartbeat()) {
DefaultFuture.received(channel, response);
}
}
进DefaultFuture
public static void received(Channel channel, Response response) {
try {
//收到响应删除future
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) {
//DubboClientHandler线程唤醒调用线程
done.signal();
}
} finally {
lock.unlock();
}
if (callback != null) {
invokeCallback(callback);
}
}
可以看到实际是两个线程在通信,main线程发送调用请求,并阻塞,DubboClientHandler线程接收到响应,并唤醒主线程。
DubboClientHandler是一个线程池,它执行工作队列中的任务(即ChannelEventRunnable对象)。
又是谁把ChannelEventRunnable对象放到DubboClientHandler线程池的工作队列中的呢?答案是New I/O client worker

//AllChannelHandler类
public void received(Channel channel, Object message) throws RemotingException {
//DubboClientHandler线程池
ExecutorService cexecutor = getExecutorService();
try {
cexecutor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
throw new ExecutionException(message, channel, getClass() +
" error when process received event .", t);
}
}
如果一个方法没有返回值,声明为:public void sayHello();
但配置不是oneway,那么cosumer还是会返回RpcResult,但是RpcResult中的内容为空。
consumer发送请求,接收响应的更多相关文章
- 使用HttpClient发送请求接收响应
1.一般需要如下几步:(1) 创建HttpClient对象.(2)创建请求方法的实例,并指定请求URL.如果需要发送GET请求,创建HttpGet对象:如果需要发送POST请求,创建HttpPost对 ...
- fiddler模拟发送请求和响应
iddler模拟发送请求和响应 一.fiddler模拟发送请求 1.fiddler模拟发送get请求 1)例如:访问博客园https://www.cnblogs.com/,并且登录输入密码账号登录,再 ...
- Postman使用手册1——导入导出和发送请求查看响应
导读: 现在的web和移动开发,常常会调用服务器提供restful接口进行数据请求,为了调试,一般会先用工具进行测试,通过测试后才开始在开发中使用.这里介绍一下如何在chrome浏览器利用postma ...
- 发送请求获取响应内容(c#)
C#请求url,获取响应的脚本 public string ResultOfApi(string url) { //发送请求 HttpWebRequest request = null; HttpWe ...
- XMLHttpRequest发送请求
*open(method,url,async) *send(string)//在使用get请求的时候,是没有主体的,所有的信息都会拼在url当中,所以使用send的时候括号里的string可以为空!如 ...
- Android HTTP实例 发送请求和接收响应
Android HTTP实例 发送请求和接收响应 Android Http连接 实例:发送请求和接收响应 添加权限 首先要在manifest中加上访问网络的权限: <manifest ... & ...
- 使用HttpClient发送请求、接收响应
使用HttpClient发送请求.接收响应很简单,只要如下几步即可. 1.创建HttpClient对象. CloseableHttpClient httpclient = HttpClients.c ...
- javascript实现select菜单/级联菜单(用Rails.ajax实现发送请求,接收响应)
在购物网站,填写收货地址的时候,会出现XX省XX市XX区的下拉菜单,如何实现此功能?思路是什么? 功能设置: 当选择省select菜单后,市的select菜单为这个省的城市列. 当选择市菜单后,区菜单 ...
- JQuery Ajax 发送请求成功后却接收不到任何响应数据问题
问题描述 使用 JQuery Ajax 向后端服务器发送请求,服务器也收到请求返回了响应数据,但是 Ajax 却收不到任何响应数据. 举例如下: $.ajax({ type: "post&q ...
随机推荐
- ssh连接linux服务器不断开- "Write failed: Broken pipe"
我自己用阿里云的服务器的时候,发现ssh连上以后,一会不用就断掉了,非常不方便,服务端的系统是ubuntu. 查了些东西,原来可以去配置服务端的sshd,或者客户端的ssh,就行了. 1,配置服务器端 ...
- 10:Python2与Python3比较
1.print 函数 1. print语句没有了,取而代之的是print()函数. Python 2.6与Python 2.7部分地支持这种形式的print语法. 2.Unicode 1. 在pyt ...
- IMAP协议命令(详细)
参照:http://www.cnblogs.com/qiubole/archive/2007/11/23/970180.html 转载:http://blog.sina.com.cn/s/blog_5 ...
- Python3基础 file with 配合文件操作
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- git如何生成指定两个commit之间的补丁
答:git format-patch <base commit id>..<latest commit id> 如git log输出以下内容: commit 2222222 y ...
- HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解
Eight Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
- 帮助大家理解java中的随机和继承,动态绑定.
package com.ykmimi.javabianchengsixiang; /** * 形状的继承 随机形状生成器 * @author ukyor */ import java.util.Ran ...
- 自整理的jquery.Validate验证表达式
自整理几个jquery.Validate验证正则: 1. 只能输入数字和字母 /^[0-9a-zA-Z]*$/g jQuery.validator.addMethod("letters ...
- Python CSV Reader/Writer 例子--转载
CSV(comma-separated values) 是跨多种形式导入导出数据的标准格式,比如 MySQL.Excel. 它以纯文本存储数和文本.文件的每一行就代表一条数据,每条记录包含了由逗号分隔 ...
- canvas和图片之间的互相装换
canvas和图片之间的互相装换 一.总结 一句话总结:一个是canvas的drawImage方法,一个是canvas的toDataURL方法 canvas drawImage() Image对象 c ...