博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Dubbo之telnet实现
阅读量:3727 次
发布时间:2019-05-22

本文共 8818 字,大约阅读时间需要 29 分钟。

Dubbo之telnet实现

更多干货

我们可以通过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
channels; //
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传递信息

那么这边的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一级的说明

回到TelnetHandler的spi文件

clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandlerexit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandlerhelp=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandlerstatus=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandlerlog=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandlerls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandlerps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandlercd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandlerpwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandlerinvoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandlertrace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandlercount=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
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
> table = new ArrayList
>(); List
handlers = extensionLoader.getActivateExtension(channel.getUrl(), "telnet"); if (handlers != null && handlers.size() > 0) { for (TelnetHandler handler : handlers) { Help help = handler.getClass().getAnnotation(Help.class); List
row = new ArrayList
(); 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指令的功能以及详细方法打印。

转载地址:http://yclnn.baihongyu.com/

你可能感兴趣的文章
python3-matplotlib自学笔记
查看>>
ROS机器人操作系统入门--(一)ROS介绍与安装
查看>>
Wifi密码攻击实验
查看>>
cryptool1使用教程
查看>>
java+serlvet+ajax+session实现登录注销
查看>>
EEE模式的3DES安全性分析
查看>>
Python为什么要使用虚拟环境-Python虚拟环境的安装和配置-virtualenv
查看>>
你们会选择哪种深度学习开源框架?Pytorch还是Caffe、TensorFlow?各家的优缺点都有哪些?
查看>>
Docker-部署Java项目
查看>>
SpringBoot使用AOP做系统日志
查看>>
确保安全的HTTPS
查看>>
HTTP确认访问用户身份的认证
查看>>
SpringBean的生命周期
查看>>
github下载的几种加速方法
查看>>
git托管代码到GitHub和Gitee(码云)
查看>>
STM32CubeMX-6.1.1 编写 stm32H743IIT6 生成keil工程时出现错误
查看>>
多文件编译写法
查看>>
操作系统--中断与系统调用
查看>>
Error running ‘ ‘D:/openjdk-16.0.1_windows-x64_bin/jdk-16.0.1/bin‘ is not a valid JRE home
查看>>
一行只能放一个元素,搜索框输入框el-input不能调整大小,el-col,el-row的形式失效.....等问题-elemntui样式为引入---某坑记录指南
查看>>