文档章节

Dubbo之telnet实现

Mr_Qi
 Mr_Qi
发布于 2017/07/15 17:38
字数 929
阅读 252
收藏 3

我们可以通过telnet来访问道对应dubbo服务的信息

比如

我们可以利用一些指令来访问。

我们知道,默认情况下,dubbo使用netty做transport。

那么dubbo是如何区分开正常业务请求和telnet请求呢?

首先来看一下netty的服务。

NettyServer在打开是会注册一些downStream和upStream的event

public class NettyServer extends AbstractServer implements Server {
     
    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
 
    private Map<String, Channel>  channels; // <ip:port, channel>
 
    private ServerBootstrap                 bootstrap;
 
    private org.jboss.netty.channel.Channel channel;
 
    public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }
 
    @Override
    protected void doOpen() throws Throwable {
        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());
    }

其中decoder和encoder对应加解码,这边也对应了之前调用异常时无法通过attachment传递信息Dubbo自定义异常message过长解决

那么这边的nettyHandler最终通过层层包装委托的机制其实到了真正执行的应该是

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);
    }
}

其中根据请求message的类型进行了区分,如果是request则进行正常的业务调用,如果是String则进行telnet的回复。

这边的handler类型是ExchangeHandlerAdapter及其对应的子类。

可以确认调用telnet时继续根据spi的方法来查找对应的实现

public String telnet(Channel channel, String message) throws RemotingException {
    String prompt = channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);
    boolean noprompt = message.contains("--no-prompt");
    message = message.replace("--no-prompt", "");
    StringBuilder buf = new StringBuilder();
    message = message.trim();
    String command;
    if (message.length() > 0) {
        int i = message.indexOf(' ');
        if (i > 0) {
            command = message.substring(0, i).trim();
            message = message.substring(i + 1).trim();
        } else {
            command = message;
            message = "";
        }
    } else {
        command = "";
    }
    if (command.length() > 0) {
        if (extensionLoader.hasExtension(command)) {
            try {
                String result = extensionLoader.getExtension(command).telnet(channel, message);
                if (result == null) {
                    return null;
                }
                buf.append(result);
            } catch (Throwable t) {
                buf.append(t.getMessage());
            }
        } else {
            buf.append("Unsupported command: ");
            buf.append(command);
        }
    }
    if (buf.length() > 0) {
        buf.append("\r\n");
    }
    if (prompt != null && prompt.length() > 0 && ! noprompt) {
        buf.append(prompt);
    }
    return buf.toString();
}

可想而知spi在dubbo服务中完全是非常大规模的使用,可以在项目中借鉴,这也是一种很典型的控制反转 详细查看filter一级的说明 

dubbo源码系列之filter的前生

回到TelnetHandler的spi文件

clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler
exit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler
help=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler
status=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
log=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandler
ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler

很明显这些就是telnet所提供的具体的指令。

这边以Help为例

@Activate
@Help(parameter = "[command]", summary = "Show help.", detail = "Show help.")
public class HelpTelnetHandler implements TelnetHandler {
     
    private final ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);
 
    public String telnet(Channel channel, String message) {
        if (message.length() > 0) {
            if (! extensionLoader.hasExtension(message)) {
                return "No such command " + message;
            }
            TelnetHandler handler = extensionLoader.getExtension(message);
            Help help = handler.getClass().getAnnotation(Help.class);
            StringBuilder buf = new StringBuilder();
            buf.append("Command:\r\n    ");
            buf.append(message + " " + help.parameter().replace("\r\n", " ").replace("\n", " "));
            buf.append("\r\nSummary:\r\n    ");
            buf.append(help.summary().replace("\r\n", " ").replace("\n", " "));
            buf.append("\r\nDetail:\r\n    ");
            buf.append(help.detail().replace("\r\n", "    \r\n").replace("\n", "    \n"));
            return buf.toString();
        } else {
            List<List<String>> table = new ArrayList<List<String>>();
            List<TelnetHandler> handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet");
            if (handlers != null && handlers.size() > 0) {
                for (TelnetHandler handler : handlers) {
                    Help help = handler.getClass().getAnnotation(Help.class);
                    List<String> row = new ArrayList<String>();
                    String parameter = " " + extensionLoader.getExtensionName(handler) + " " + (help != null ? help.parameter().replace("\r\n", " ").replace("\n", " ") : "");
                    row.add(parameter.length() > 50 ? parameter.substring(0, 50) + "..." : parameter);
                    String summary = help != null ? help.summary().replace("\r\n", " ").replace("\n", " ") : "";
                    row.add(summary.length() > 50 ? summary.substring(0, 50) + "..." : summary);
                    table.add(row);
                }
            }
            return "Please input \"help [command]\" show detail.\r\n" + TelnetUtils.toList(table);
        }
    }
 
}

根据Help的注解,将各个telnet指令的功能以及详细方法打印。

© 著作权归作者所有

共有 人打赏支持
Mr_Qi

Mr_Qi

粉丝 280
博文 359
码字总数 369228
作品 0
南京
程序员
私信 提问
Dubbo之telnet实现

我们可以通过telnet来访问道对应dubbo服务的信息 比如 1737165qEu871390.png 我们可以利用一些指令来访问。 我们知道,默认情况下,dubbo使用netty做transport。 那么dubbo是如何区分开正常业...

波波维奇
2017/11/30
0
0
阿里巴巴开源服务框架 Dubbo 2.0.10 发布

Dubbo是阿里巴巴SOA服务化治理方案的核心框架,每天为1,000+个服务提供1,000,000,000+次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。 2011-12-28发布最新可靠版本2.0.10,该版本修...

SDK4
2011/12/28
16.6K
11
史上最全 40 道 Dubbo 面试题及答案,看完碾压面试官!

想往高处走,怎么能不懂 Dubbo? Dubbo是国内最出名的分布式服务框架,也是 Java 程序员必备的必会的框架之一。Dubbo 更是中高级面试过程中经常会问的技术,无论你是否用过,你都必须熟悉。 ...

Java技术栈
2018/10/02
0
0
Dubbo剖析-Dubbo协议

一、前言 TCP协议栈中,每层模型都有自己的协议报文格式,TCP协议是网络七层模型中的传输层,在TCP上层是应用层,应用层协议常见的有telnet等,Dubbo协议作为建立在TCP协议之上的一种协议,自...

加多
2018/01/04
0
0
基于ZooKeeper的Dubbo注册中心【转】

Zookeeper注册中心安装 建议使用dubbo-2.3.3以上版本的zookeeper注册中心客户端。Zookeeper是Apache Hadoop的子项目,强度相对较好,建议生产环境使用该注册中心。Dubbo未对Zookeeper服务器端...

看到了打开了
2016/10/25
34
1

没有更多内容

加载失败,请刷新页面

加载更多

eggjs与sequelize简单demo

参考 egg 官方文档 安装 // 依赖npm install --save egg-sequelize mysql2// ts 类型npm install --save @types/sequelize 插件,config/plugin.ts import { EggPlugin } from 'egg';......

Geeyu
57分钟前
1
0
看过上百部片子的这个人教你视频标签算法解析

本文由云+社区发表 随着内容时代的来临,多媒体信息,特别是视频信息的分析和理解需求,如图像分类、图像打标签、视频处理等等,变得越发迫切。目前图像分类已经发展了多年,在一定条件下已经...

腾讯云加社区
今天
4
0
2. 红黑树

定义:红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树(Binary Search Tree)。 要理解红黑树,先要了解什么是二叉查找树。在上一章中,我们学习了什么是二叉树,以及二叉树...

火拳-艾斯
今天
3
0
input的button类型,点击页面跳转

一、input type=button 不做任何操作 例如: <input type="button" class="btn btn-primary" style="width: 30%" value="返回" onclick="window.location.href='/users/list'"></input> onc......

Sunki
今天
1
0
踩坑:js 小数运算出现精度问题

背景 在学习小程序商城源码时发现了这个问题,单价可能出现小数,小数之间运算结果会莫名其妙多出一大串数字,比如下面这样👇。 在此之前我是知道 js 中著名的 0.1 + 0.2 != 0.3 的问题的,...

dkvirus
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部