dubbo transport网络传输层
来源:互联网 发布:js 判断鼠标滑动方向 编辑:程序博客网 时间:2024/04/28 19:45
NettyHandler:
继承netty对象SimpleChannelHandler,重写了channelConnected、channelDisconnected、messageReceived、writeRequested、exceptionCaught方法,当netty的通道发生连接、断开连接、收到消息、写入消息、捕获异常等事件时触发NettyHandler中的对应方法。在这些方法中后续的处理链为NettyServer/NettyClient->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->Decodehandler->HeaderExchangerHandler->DubboProtocol$requestHandler。
@Override protected void doOpen() throws Throwable {//NettyClient NettyHelper.setNettyLoggerFactory(); bootstrap = new ClientBootstrap(channelFactory); // config // @see org.jboss.netty.channel.socket.SocketChannelConfig bootstrap.setOption("keepAlive", true); bootstrap.setOption("tcpNoDelay", true); bootstrap.setOption("connectTimeoutMillis", getTimeout()); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this); ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); }
@Override protected void doOpen() throws Throwable {//NettyServer NettyHelper.setNettyLoggerFactory(); ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true)); ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true)); ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS)); bootstrap = new ServerBootstrap(channelFactory); final NettyHandler nettyHandler = new NettyHandler(getUrl(), this); channels = nettyHandler.getChannels(); // https://issues.jboss.org/browse/NETTY-365 // https://issues.jboss.org/browse/NETTY-379 // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true)); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() { NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this); ChannelPipeline pipeline = Channels.pipeline(); /*int idleTimeout = getIdleTimeout(); if (idleTimeout > 10000) { pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0)); }*/ pipeline.addLast("decoder", adapter.getDecoder()); pipeline.addLast("encoder", adapter.getEncoder()); pipeline.addLast("handler", nettyHandler); return pipeline; } }); // bind channel = bootstrap.bind(getBindAddress()); }通过上面NettyClient和NettyServer的源代码,可以看到在pipeline中都添加了一个nettyHandler,由于nettyHandler会捕获Netty通道的事件,里面会调用NettyClient/NettyServer的connected、disconnected等方法,先以NettyClient.connected方法分析.
在调用到DubboProtocol的refer->getClients->initClient,在initClient内部会调用Exchanger.connect(url,requesthandler),这个handler内容如下:
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { public Object reply(ExchangeChannel channel, Object message) throws RemotingException { if (message instanceof Invocation) {//只处理远程调用消息Invocation Invocation inv = (Invocation) message; Invoker<?> invoker = getInvoker(channel, inv); //如果是callback 需要处理高版本调用低版本的问题 if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))){ String methodsStr = invoker.getUrl().getParameters().get("methods"); boolean hasMethod = false; if (methodsStr == null || methodsStr.indexOf(",") == -1){ hasMethod = inv.getMethodName().equals(methodsStr); } else { String[] methods = methodsStr.split(","); for (String method : methods){ if (inv.getMethodName().equals(method)){ hasMethod = true; break; } } } if (!hasMethod){ logger.warn(new IllegalStateException("The methodName "+inv.getMethodName()+" not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) +" ,invocation is :"+inv ); return null; } } RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress()); return invoker.invoke(inv); } throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress()); } @Override public void received(Channel channel, Object message) throws RemotingException { if (message instanceof Invocation) { reply((ExchangeChannel) channel, message); } else { super.received(channel, message); } } @Override public void connected(Channel channel) throws RemotingException { invoke(channel, Constants.ON_CONNECT_KEY); } @Override public void disconnected(Channel channel) throws RemotingException { if(logger.isInfoEnabled()){ logger.info("disconected from "+ channel.getRemoteAddress() + ",url:" + channel.getUrl()); } invoke(channel, Constants.ON_DISCONNECT_KEY); } private void invoke(Channel channel, String methodKey) { Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey); if (invocation != null) { try { received(channel, invocation); } catch (Throwable t) { logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t); } } } private Invocation createInvocation(Channel channel, URL url, String methodKey) { String method = url.getParameter(methodKey); if (method == null || method.length() == 0) { return null; } RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]); invocation.setAttachment(Constants.PATH_KEY, url.getPath()); invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY)); invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY)); invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY)); if (url.getParameter(Constants.STUB_EVENT_KEY, false)){ invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString()); } return invocation; } };
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); }
在调用Transporters.connect时会触发NettyTransporter.connect->NettyClient的构造函数->wrapChannelHandler函数->ChannelHandlers.wrap->ChannelHandlers->wrapInternal
protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) { return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class) .getAdaptiveExtension().dispatch(handler, url))); }
从这里可以看到,又在原来handler的基础上封装了AllChannelHandler->HeratbeatHandler->MutliMessageHandler。
所以整个处理链为NettyServer/NettyClient->MutliMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangerHandler->DubboProtocol$requestHandler。
MutliMessageHandler
对于消息的received事件,如果消息是指传输的,解码后会由MultiMessage承载,并遍历其中的消息,交由下一个handler处理。
@SuppressWarnings("unchecked")@Override public void received(Channel channel, Object message) throws RemotingException { if (message instanceof MultiMessage) { MultiMessage list = (MultiMessage)message; for(Object obj : list) { handler.received(channel, obj); } } else { handler.received(channel, message); } }
HeartbeatHandler
设置通道的读定时间戳。
(1)对于connected事件,设置通道的读写时间戳为当前时间。
(2)对于disconnected事件,清空读写时间戳。
(3)对于sent事件,设置通道的写时间戳为当前时间。
(4)对于received事件,设置通道的读时间戳为当前时间;若消息是心跳消息请求(mEvent=true and mData=null),如果此心跳事件需要响应(mTwoWay=true),则构建响应对象Response,并发送响应消息不做后续channel处理;若消息是心跳响应(mEvent=true and mData=null),记录日志直接返回,不在做后续的channel处理,若不是心跳消息,则交下一个处理器处理。
Dispatcher创建的Handler处理器
根据URL参数dispatcher选择Dispatcher类,不同的Dispatcher创建不同的Handler,表示通道的事件的处理模式,在这些Handler中构建异步执行任务ChannelEventRunnable,异步执行connected、disconnected、received、caught、sent等操作。目前支持四种Handler处理器:
(1)AllChannelHandler:除发送消息之外,所有的事件都采用线程池处理。
(2)ExecutionChannelHandler:与AllChannelHandler不同之处,若创建的线程池ExecutorService不可用,AllChannelHandler将使用共享线程池,而ExecutionChannelHandler只有报错。
(3)ChannelHandler:所有事件都真接有channelhandler处理,不采用线程池。
(4)MessageOnlyChannelHandler:只有接受消息采用线程池。
(5)ConnectionOrderedChannelHandler:单线程,排队处理。
在上述的Handler的初始化过程中,会根据url的参数threadpool来创建线程份,目前支持的线程池类有三种,默认FixedThreadPool。
FixedThreadPool:此线程池启动时即创建固定大小的线程数,不做任何伸缩。
CachedThreadPool:此线程池可伸缩,线程空闲一分钟后回收,新请求重新创建线程。
LimitedThreadPool:此线程一直增长,直到上限,增长后不收缩。
DecodeHandler
对读取的消息进行解码操作。
HeaderExchangerHandler
处理connected、disconnected、sent、received、caught等事件,并调用DubboProtocol$requestHandler内部响应的事件方法。
(1)connected:更新通道的读取时间戳和写入时间戳(这和HeartbeatHandler有点重复啊?),调用DubboProtoco$requestHandler匿名内部类的connected事件方法。
(2)disconnected:清空读写时间戳(和HeartbeatHandler一样?),调用DubboProtocol$requestHandler的disconnected方法。
(3)sent:更新通道的写入时间戳,回调用DubboProtocol$requestHandler的sent,由于requestHandler没有重写sent方法,即调用其父类ChannelHandlerAdapter 的该方法,这是一个空方法。
public void sent(Channel channel, Object message) throws RemotingException {//HeaderExchangeHandler Throwable exception = null; try { channel.setAttribute(KEY_WRITE_TIMESTAMP, System.currentTimeMillis()); ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); try { handler.sent(exchangeChannel, message); } finally { HeaderExchangeChannel.removeChannelIfDisconnected(channel); } } catch (Throwable t) { exception = t; } if (message instanceof Request) { Request request = (Request) message; DefaultFuture.sent(channel, request); } if (exception != null) { if (exception instanceof RuntimeException) { throw (RuntimeException) exception; } else if (exception instanceof RemotingException) { throw (RemotingException) exception; } else { throw new RemotingException(channel.getLocalAddress(), channel.getRemoteAddress(), exception.getMessage(), exception); } } }
如果消息是Request,即发送请求,则根据请求的唯一标识ID获取DefaultFuture对象,并设置发送时间为当前时间,用于在超时之后分析客户端的耗时、服务端的耗时。
(4)更新通道的读取时间戳。
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("hsf 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); } }
如果是Request请求,并且需要对请求做回执时,handleRequest会调用DubboProtocol$requestHandler的.reply方法,在该方法中完成服务端本地方法调用并返回结果,然后handleRequest方法中根据Request对象的ID构建Response对象,将回调用服务的执行结果作为Response对象的结果字段;最后调用channel.send方法向客户端发送响应消息。如果是Request对象并且不需要对请求做回执时,则调用DubboProtocol$requestHandler匿名内部类received方法,如果是Response且不是心跳响应,则根据response中的ID获取DefaultFuture对象,若DefaultFuture对象不存在,则该响应已经超时并且已经被删除了,若还存在,则设置response的值,唤醒线程,DefaultFuture对象的get方法返回调用值。
DubboProtocol$requestHandler
(1)connected:检查URL中是否有onconnect参数,若有则回调用该参数指定的方法。
(2)disconnected:检查URL中是否有ondisconnect参数,若在则回调该参数指定的方法。
(3)received:若消息是Invocation对象则调用reply方法,根据生成的serviceKey在AbstractProtocol.exporterMap中查找DubboExporter对象,然后根据此exporter对象获取服务创建的invoker对象,从而完成服务的本地方法调用;若不是则调用父类的received方法,不进行任何处理。
阅读全文
0 0
- dubbo transport网络传输层
- Dubbo——Transport网络传输层
- dubbo_网络传输transport
- 传输层(Transport Layer)
- 网络 运输层(Transport)
- 基础计算机网络——传输层(Transport Layer)笔记
- Thrift源码分析(六)-- Transport传输层分析
- TLS:安全传输层协议 TLS:Transport Layer Security
- TLS 传输层安全协议 Transport Layer Security 原理简述
- 网络传输层、服务器程序
- 网络传输层工作原理
- 网络传输层的基本知识
- 网络层其他传输协议
- 传输层 和网络层的区别
- 数据路链路层、网络层、传输层
- 计算机网络:传输层和网络层
- 网络层和传输层的区别
- 网络层协议和传输层协议
- Angular 网络请求404
- 第八天:ROI与mask掩码
- Ubuntu网络问题解决教程
- java基础十二天 常用api
- Node.js基本模块stream
- dubbo transport网络传输层
- 如何获取window.location.search中每一个查询参数函数的封装
- ABP入门系列目录——学习Abp框架之实操演练(推荐)
- Number Sequence 【hdu-1711】【KMP】
- android7.0 打开附件
- ubuntu下vim的安装及常用编程习惯设置
- android 相机开发
- C#通过程序内部注册ACTIVE控件
- stm32学习笔记(十)输入捕获实验