深度解析dubbo在线运维Qos

文章目录

注:本文基于dubbo v2.6.1

1. dubbo的Qos

QoS的英文全称为"Quality of Service",中文名为"服务质量"。在dubbo 2.5.8 新版本增加了 QOS 模块,提供了新的 telnet 命令支持。dubbo管它叫在线运维命令,我们可以通过它能够看到服务提供者状态,服务调用者状态,现在dubbo提供了 ls , online,offline,help ,quit命令。

命令

用途

ls

能够列出来该实例服务提供者与调用者状态

online

服务上线,可以指定某个接口,也可以什么也不指定,这样就是全部

offline

服务下线,可以指定某个接口,也可以什么也不指定,这样就是全部

help

查看命令的用途,不带参数显示全部命令,带参数只显示指定的

quit

退出Qos

2.dubbo Qos简单使用

我们可以在dubbo的配置文件配置参数,我这里是使用的xml的形式,其他形式可以看下官网文档:官网文档(文档讲的很详细,而且是中文)

<dubbo:application name="dubbo-consumer">
    <dubbo:parameter key="qos.enable" value="true"/>
    <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
    <dubbo:parameter key="qos.port" value="8108"/>
</dubbo:application>

qos.enable :表示是否开始Qos
qos.accept.foreign.ip: 允许访问的ip,缺省就是false,表示任何ip都可访问
qos.port: Qos提供服务的端口
接下来我启动服务,然后在终端上使用telnet的方式连接dubbo Qos
在这里插入图片描述

2.1 ls

可以看到服务提供服务与调用服务的状态
这个 Provider Service Name 就是服务提供者名字 , PUB 就是状态 N是未注册,就是没有注册到注册中心,其实服务下线功能就是从注册中心unregister ,Y表示服务在线,就是注册到注册中心了。
在这里插入图片描述

2.2 online

服务上线,就是将服务注册到注册中心上去,后面可以带个参数,参数是指定具体上线的接口,不带参数表示全部服务接口(这个得是你标识的,比如说使用了dubbo @Service注解)都注册到注册中心去。
我们ls小节的时候可以看到com.xuzhaocai.dubbo.provider.IUserInfoService 是N,我们可以使用online来进行服务上线
在这里插入图片描述
在这里插入图片描述
我们可以看到服务状态是Y了,这其实就是注册中心 调用register(),我们在后面可以看到具体代码实现。

2.3 offline

offline 是服务下线,跟online一个使用方法,这里就不再赘述了。

2.4 help

帮助,可以指定具体的cmd,这样就显示指定cmd的帮助
在这里插入图片描述
不指定的话显示全部的命令
在这里插入图片描述

2.5 quit

退出Qos,你不用这个quit可以试试,看看能不能退出去,我mac直接使用ctrl+c 反正不行。
注: 我这里就不再详细说明这个用法了,这东西官网文档很详细,而且使用也简单,主要是场景 ,直接源码搞起

3.dubbo Qos 源码剖析

3.1 Qos服务器创建时机

其实在dubbo进行服务暴露与服务引用的时候,我们都见过Exporter<?> exporter = protocol.export(wrapperInvoker); (com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol)或者refprotocol.refer(interfaceClass, urls.get(0));(com.alibaba.dubbo.config.ReferenceConfig#createProxy) ,我们知道这个protocol成员是这个样子
在这里插入图片描述
根据dubbo spi 自适应的规则,实际上执行的是 url中protocol 参数值对应的那个实现类,我们这个场景下就是RegistryProtocol 实现类,然后dubbo spi 还会帮你setter 注入 ,wrapper 包装
我们可以看下都有哪些Protocol 包装实现类
在这里插入图片描述
v2.6.1 这个版本就这些。
我们获得的RegistryProtocol对象,其实外面是包了这几个wrapper类,就像下图这个样子(当然这个顺序不一定是这个样子的,他这边没有对顺序做要求)
在这里插入图片描述
所以我们在服务暴露或者服务引用的时候,就会走这个QosProtocolWrapper类。

3.2 QosProtocolWrapper

在这里插入图片描述
我们可以看到export与refer方法,判断如果protocol=registry,接着进入这个startQosServer(url)
看下这 startQosServer方法。
在这里插入图片描述
这个方法中我们可以看到就是判断qos.enable 启动参数,然后 判断是否已经启动,这个玩意一个服务实例就只能启动一次,再就是获得一个Server对象,将用户设置的一些参数值设置进去,然后调用start方法。
我们来看下这个Server类

3.3 com.alibaba.dubbo.qos.server.Server

在这里插入图片描述
我们这里是创建了一个netty的server,开始的时候使用cas判断启动状态,添加了一个QosProcessHandler消息处理类,我们的命令就是到这里面处理的。我们看下这个QosProcessHandler 类,传入参数就是welcome欢迎语跟允许的ip。
我们来看下的这个QosProcessHandler消息处理类

3.4 com.alibaba.dubbo.qos.server.handler.QosProcessHandler

当消息连接上的时候,会调用channelActive方法,我们可以看到,它将这个welcome 欢迎语与 dubbo> 这个开始头发出去了。
在这里插入图片描述
也就是这个东西
在这里插入图片描述
接着我们再输入命令的时候,就会到decode 方法中,首先判断buffer里面有没有能读的字节,如果没有直接返回,获取一个字节,判断是不是http请求,因为Dubbo Qos 是支持http 与telnet的。
在这里插入图片描述
这里就会相应的添加handler,我们可以看到,他这个handler是动态添加与删除的,每一次访问都要添加与删除。
我们这里使用的是telnet ,我们看下telnet的是怎样实现的,http 的也就同理了。
接下来我们看下这个TelnetProcessHandler 处理类,那四个都是netty的,做相应辅助功能的,比如说使用行解析,使用utf-8解码,然后空闲时间,TelnetProcessHandler 是主要处理我们命令的

 @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

        if (StringUtils.isBlank(msg)) {// 是否是空消息
            ctx.writeAndFlush(QosProcessHandler.prompt);// 将dubbo>   写回去
        } else {
            CommandContext commandContext = TelnetCommandDecoder.decode(msg);
            commandContext.setRemote(ctx.channel());// 设置channel

            try {
                String result = commandExecutor.execute(commandContext);
                if (StringUtils.equals(QosConstants.CLOSE, result)) {// 是否返回close
                    ctx.writeAndFlush(getByeLabel()).addListener(ChannelFutureListener.CLOSE);
                } else {// 将结果写回去   xxx /r/n  dubbo>
                    ctx.writeAndFlush(result + QosConstants.BR_STR + QosProcessHandler.prompt);
                }
            } catch (NoSuchCommandException ex) {
                ctx.writeAndFlush(msg + " :no such command");
                ctx.writeAndFlush(QosConstants.BR_STR + QosProcessHandler.prompt);
                log.error("can not found command " + commandContext, ex);
            } catch (Exception ex) {
                ctx.writeAndFlush(msg + " :fail to execute commandContext by " + ex.getMessage());
                ctx.writeAndFlush(QosConstants.BR_STR + QosProcessHandler.prompt);
                log.error("execute commandContext got exception " + commandContext, ex);
            }
        }
    }

这里首先判断消息是不是空,空消息就将 dubbo>这个返回给客户端
然后将消息解析成 CommandContext, 也就是CommandContext commandContext = TelnetCommandDecoder.decode(msg); 这句,就是将消息解析成cmd与参数,调用CommandContext工厂创建CommandContext 对象。在这里插入图片描述
接着就是使用CommandExecutor 执行这个命令,也就是这句String result = commandExecutor.execute(commandContext);
我们来看下这个DefaultCommandExecutor的execute方法,先是通过命令获取对应的BaseCommand实现类,没有找到对应的话就抛出NoSuch的异常。有的话直接调用execute执行。
在这里插入图片描述
这个BaseCommand我们后面再说,这个执行结果返回,接着回到TelnetProcessHandler 的channelRead0方法中,如果这个执行结果是close的话,就写回"BYE! ";这个字符串,并且添加ChannelFutureListener.CLOSE 这个listener。
如果是正常执行结果的话,写回去就可以了。
现在我们来看下这个BaseCommand的定义以它的子类们

3.4 BaseCommand

这个是就是命令的接口,是基于dubbo spi的,所有Qos命令都要实现它,然后重写execute方法。
在这里插入图片描述
看下它的实现类
在这里插入图片描述
下面我们就挨个看看

3.4.1 ls

ls 这个命令是获取该实例服务提供者接口与调用者接口及状态。
我们来看看代码实现
在这里插入图片描述
调用了listProvider()获取了服务提供者接口,调用了服务调用者接口。
我们这里拿获取服务提供者接口看看:

 public String listProvider() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("As Provider side:
");//通过ApplicationModel获取所有的provider,这个是服务注册上的时候添加到ApplicationModel 里面的
        Collection<ProviderModel> ProviderModelList = ApplicationModel.allProviderModels();
        TTable tTable = new TTable(new TTable.ColumnDefine[]{
                new TTable.ColumnDefine(TTable.Align.MIDDLE),
                new TTable.ColumnDefine(TTable.Align.MIDDLE)
        });
        //Header
        tTable.addRow("Provider Service Name", "PUB");
        //Content
        for (ProviderModel providerModel : ProviderModelList) {
            tTable.addRow(providerModel.getServiceName(), isReg(providerModel.getServiceName()) ? "Y" : "N");
        }
        stringBuilder.append(tTable.rendering());

        return stringBuilder.toString();
    }

这边有两个点: 一个是通过ApplicationModel 获取所有的provider,这个ApplicationModel 类,里面其实维护了几个map,其中就有本实例服务提供者与服务调用者接口列表,当我们在暴露服务完成的时候,就会向ApplicationModel 这个类里面添加对应的服务提供者的信息,也就是走的这个方法
在这里插入图片描述
每当我们服务引用完,创建完proxy代理类的时候,就会往ApplicationModel 这个类里面添加对应的服务调用者的信息,也就是走的这个方法
在这里插入图片描述
所以我们就能拿到本实例的服务提供者接口了。接着往下看就是创建TTable ,是两列的,然后遍历服务提供者接口列表添加行。
到最后就是rendering 渲染了,在循环的时候其实还有一个点,使用isReg(providerModel.getServiceName()) “Y” : “N”) 是否在线,这里面判断的其实还是invoker包装类的isReg,注册到注册中心后设置这个标识。
在这里插入图片描述
同理本实例服务提供者也是这个样子获取的,到这里我们这个ls命令处理就完成了。

3.4.2 online

com.alibaba.dubbo.qos.command.impl.Online 服务上线。
在这里插入图片描述
这个online 命令使用参数的,如果用户没有指定,就是.*,在下面匹配上就会匹配到。
首先是判断参数,接着就是从ApplicationModel 获取本实例服务提供者列表,遍历,看看是否符合你传过来的参数,,符合就设置hasService = true,接着就是根据serviceName从本地注册表中获取对应的ProviderInvokerWrapper列表,遍历判断如果对应的状态是在线的(这里还是判断的isReg),就跳过,否则获取对应的注册中心,进行注册,同时将isReg状态设置成true。

3.4.3 offline

com.alibaba.dubbo.qos.command.impl.Offline 服务下线,我们看下代码,它这个与online差不多,它这个循环判断服务没有在线就跳过去,在线的就调用注册中心的unregister()方法进行服务下线,同时设置isReg服务在线状态为false。
在这里插入图片描述

3.4.4 help

com.alibaba.dubbo.qos.command.impl.Help 帮助文档作用,可以传参数 指定需要帮助的命令,也可以不指定,那就是显示全部命令的帮助。直接上代码
在这里插入图片描述
我们看下这行所有命令的,这个指定命令的原理也差不多
在这里插入图片描述
就是使用CommandHelper.getAllCommandClass() 获取所有的BaseCommand 子类class对象,他这个使用dubbo spi获取所有子类的
在这里插入图片描述
然后获取子类上面的@Cmd注解,就像offline上面这个,name就是命令,summary就是说明 ,example 就是列子
在这里插入图片描述

3.4.5 quit

com.alibaba.dubbo.qos.command.impl.Quit 退出 ,这就是直接返回了close,然后TelnetProcessHandler就会处理这个close进行退出。
在这里插入图片描述

4.总结

到这里我们dubbo 在线运维Qos 就解析完成了,概括一下,首先是讲了 dubbo Qos ,然后简单使用了一下,最后就是咱们的源码解析了,从Qos服务器启动时机到每一个commad命令的执行原理。这个Qos 使用简单,主要还是它的使用场景,正如dubbo 官方文档说的,服务上线 服务下线的使用场景。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值