MQTT---HiveMQ源码详解(十三)Netty-MQTT消息、事件处理(源码举例解读)

前言

由于上一篇讲的都是大致的流程,所以这一篇我们抽取流程中的一步,给大家介绍Authentication部分的源码,让大家对上一节的理解更深。

MqttConnectHandler

MqttConnectHandler是SimpleChannelInboundHandler的子类

channelRead0

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Connect msg) throws Exception {
        try {
            
//加入MqttDisallowSecondConnect  
          
ctx.pipeline().addAfter(Pipelines.MQTT_MESSAGE_DECODER, Pipelines.MQTT_DISALLOW_SECOND_CONNECT, this.disallowSecondConnect);
        } catch (IllegalArgumentException e) {
            ctx.pipeline().firstContext().fireChannelRead(msg);
            return;
        }
        //校验clientid
        if (!validIdentifier(ctx, msg)) {
            return;
        }
        //标志是否接管
ctx.channel().attr(AttributeKeys.MQTT_TAKEN_OVER).set(false);
        //删除连接成功,未发connect消息超时handler
        removeConnectIdleHandler(ctx);
        //进入插件认证阶段
        pluginOnAuthentication(ctx, msg);
    }

pluginOnAuthentication

private void pluginOnAuthentication(ChannelHandlerContext ctx, Connect connect) {
    //获得clienttoken,ClientToken是ClientCredentials实现类
        ClientToken clientToken = ChannelUtils.clientToken(ctx.channel());
        //判断callbackRegistry中是否存在可用的OnAuthenticationCallback
        if (this.callbackRegistry.isAvailable(OnAuthenticationCallback.class)) {
            
//添加PluginOnAuthenticationCallbackHandler,
         ctx.pipeline().addLast(Pipelines.PLUGIN_ON_AUTHENTICATION_CALLBACK_HANDLER, this.pluginOnAuthenticationCallbackHandlerProvider.get());
         //触发PluginOnAuthentication事件
            ctx.fireUserEventTriggered(new PluginOnAuthentication(connect, clientToken));
        } else {
        //如果没有可用OnAuthenticationCallback,那么认为是不需要做Authentication,就去处理LWT,因为当client掉线后,会触发发送遗言,所以需要先判断对与该遗言发布到topic是否具备权限
            pluginOnAuthorizationLWT(ctx, null, connect, clientToken, ReturnCode.ACCEPTED, true);
        }
    }

userEventTriggered

@Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        //当插件认证完成
        if (evt instanceof PluginOnAuthenticationCompleted) {
        //进行认证完成后的处理
            pluginOnAuthenticationCompleted(ctx, (PluginOnAuthenticationCompleted) evt);
        } else if (evt instanceof PluginRestrictionsAfterLoginCompleted) {
            pluginRestrictionsAfterLoginCompleted(ctx, (PluginRestrictionsAfterLoginCompleted) evt);
        } else if (evt instanceof PluginOnConnectCompleted) {
            pluginOnConnectCompleted(ctx, (PluginOnConnectCompleted) evt);
        } else if (evt instanceof PluginOnAuthorizationCompleted) {
            pluginOnAuthorizationCompleted(ctx, (PluginOnAuthorizationCompleted) evt);
        } else if (evt instanceof MqttConnectPersistenceHandler.OnConnectPersistenceCompleted) {
            MqttConnectPersistenceHandler.OnConnectPersistenceCompleted event = (MqttConnectPersistenceHandler.OnConnectPersistenceCompleted) evt;
            onConnectPersistenceCompleted(ctx, event.getConnect(), event.isSessionPresent());
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

pluginOnAuthenticationCompleted

    private void pluginOnAuthenticationCompleted(ChannelHandlerContext ctx,
                                                 PluginOnAuthenticationCompleted event) {
        //获得处理完成的ReturnCode                                       
        ReturnCode returnCode = event.getReturnCode();
        boolean accepted = returnCode == ReturnCode.ACCEPTED;
        //处理LWT
        pluginOnAuthorizationLWT(ctx, event.getException(), event.getConnect(),
                event.getClientCredentials(), returnCode, accepted);
    }

PluginOnAuthenticationCallbackHandler


@Singleton
@ChannelHandler.Sharable
public class PluginOnAuthenticationCallbackHandler extends ChannelInboundHandlerAdapter {
    private static final Logger LOGGER = LoggerFactory.getLogger(PluginOnAuthenticationCallbackHandler.class);
    private final CallbackRegistry callbackRegistry;
    private final HiveMQConfigurationService hiveMQConfigurationService;
    private final Metrics metrics;
    private final CallbackExecutor callbackExecutor;

    @Inject
    public PluginOnAuthenticationCallbackHandler(CallbackRegistry callbackRegistry,
                                                 HiveMQConfigurationService hiveMQConfigurationService,
                                                 Metrics metrics,
                                                 CallbackExecutor callbackExecutor) {
        this.callbackRegistry = callbackRegistry;
        this.hiveMQConfigurationService = hiveMQConfigurationService;
        this.metrics = metrics;
        this.callbackExecutor = callbackExecutor;
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        //当需要插件认证
        if (evt instanceof PluginOnAuthentication) {
            //进行认证
            onAuthentication(ctx, (PluginOnAuthentication) evt);
        //当单一一个插件认证完成
        } else if (evt instanceof PluginOnAuthenticationCallbackCompleted) {
            //对一个插件认证完成的结果进行处理
            onAuthenticationCallbackCompleted(ctx, (PluginOnAuthenticationCallbackCompleted) evt);
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    private void onAuthentication(ChannelHandlerContext ctx, PluginOnAuthentication event) {
        //判断是否存在可用OnAuthenticationCallback.class,这里再判断一次原因是因为有时差。
        boolean available = this.callbackRegistry.isAvailable(OnAuthenticationCallback.class);
        if (available) {
        //获得所有已注册的OnAuthenticationCallback,并构建新的队列
            Deque<OnAuthenticationCallback> leftCallbacks = new ArrayDeque(this.callbackRegistry.getCallbacks(OnAuthenticationCallback.class));
            //获得认证证书
            ClientCredentials clientCredentials = event.getClientCredentials();
            //获得callback数量,并作为期待的返回结果数量,以后面处理完成作为一个判断条件
            int expectedResultCount = leftCallbacks.size();
            //poll一个OnAuthenticationCallback,进行认证
            OnAuthenticationCallback callback = leftCallbacks.poll();
            //构建存储结果的list
            List<PluginOnAuthenticationResult> results = new ArrayList(leftCallbacks.size());
            //提交认证task
            submitTask(ctx, callback, clientCredentials, event.getConnect(), leftCallbacks, results, expectedResultCount);
            //如果用户配置需要所有插件都必须全部认证通过,才认为通过认证,并发布认证完成事件事件
        } else if (needAllPluginsToReturnTrue()) {
            ctx.pipeline().fireUserEventTriggered(new PluginOnAuthenticationCompleted(
                    event.getConnect(), event.getClientCredentials(), ReturnCode.REFUSED_NOT_AUTHORIZED, new AuthenticationException("No OnAuthenticationCallback available", ReturnCode.REFUSED_NOT_AUTHORIZED)));
        } else {
        //否则,认为认证通过,并发布认证完成事件
            ctx.pipeline().fireUserEventTriggered(new PluginOnAuthenticationCompleted(
                    event.getConnect(), event.getClientCredentials(), ReturnCode.ACCEPTED));
        }
    }

    //当一个callback认证完成
    private void onAuthenticationCallbackCompleted(ChannelHandlerContext ctx,
                                                   PluginOnAuthenticationCallbackCompleted event) {
        //获得事件结果
        List<PluginOnAuthenticationResult> results = event.getResults();
        //获得最后一个result
        PluginOnAuthenticationResult lastResult = results.get(results.size() - 1);
        Connect connect = event.getConnect();
        ClientCredentials clientCredentials = event.getClientCredentials();
        //判断是否可以提前结束,也就是可以确定的到可以返回client端ConnAck
        if (lastResult.isRefused() ||
                lastResult.isAuthenticated() && !needAllPluginsToReturnTrue() ||
                !lastResult.isAuthenticated() && needAllPluginsToReturnTrue()) {
                //触发认证完成事件
            ctx.pipeline().fireUserEventTriggered(new PluginOnAuthenticationCompleted(
                    connect, clientCredentials, lastResult.getReturnCode(), lastResult.getException()));
            //判断当前pipeline中是否存在当前handler,并移除
            if (ctx.pipeline().get(getClass()) != null) {
                ctx.pipeline().remove(this);
            }
            return;
        }
        //如果所有插件认证都完成了
        if (results.size() == event.getExpectedResultCount()) {
            //如果认证通过
            if (accepted(results)) {
                ctx.pipeline().fireUserEventTriggered(new PluginOnAuthenticationCompleted(
                        connect, clientCredentials, ReturnCode.ACCEPTED));
            //否则认证失败
            } else {
                ctx.pipeline().fireUserEventTriggered(new PluginOnAuthenticationCompleted(
                        connect, clientCredentials, ReturnCode.REFUSED_NOT_AUTHORIZED));
            }
            //判断并移除
            if (ctx.pipeline().get(getClass()) != null) {
                ctx.pipeline().remove(this);
            }
            return;
        }
        //如果还有插件未完成认证
        Queue<OnAuthenticationCallback> leftCallbacks = event.getLeftCallbacks();
        //poll一个OnAuthenticationCallback
        OnAuthenticationCallback callback = leftCallbacks.poll();
        //继续提交认证task
        submitTask(ctx, callback, clientCredentials, connect,
                leftCallbacks, results, event.getExpectedResultCount());
    }

    private void submitTask(ChannelHandlerContext ctx,
                            OnAuthenticationCallback callback,
                            ClientCredentials clientCredentials,
                            Connect connect,
                            Queue<OnAuthenticationCallback> leftCallbacks,
                            List<PluginOnAuthenticationResult> results,
                            int expectedResultCount) {
        //获得到Future
        ListenableFuture future = this.callbackExecutor.submit(createTask(callback, clientCredentials));
        //创建获得结果的callback
        ResultCallback resultCallback = createResultCallback(ctx, clientCredentials, connect, leftCallbacks, results, expectedResultCount);
        //同步获得结果
        Futures.addCallback(future, resultCallback, ctx.executor().parent());
    }


    //创建认证task
    @NotNull
    @VisibleForTesting
    Task createTask(OnAuthenticationCallback callback,
                    ClientCredentials clientCredentials) {
        return new Task(callback, clientCredentials, this.metrics);
    }

    //创建获得认证结果FutureCallback
    @NotNull
    @VisibleForTesting
    ResultCallback createResultCallback(ChannelHandlerContext ctx,
                                        ClientCredentials clientCredentials,
                                        Connect connect,
                                        Queue<OnAuthenticationCallback> leftCallbacks,
                                        List<PluginOnAuthenticationResult> results,
                                        int expectedResultCount) {
        return new ResultCallback(ctx, clientCredentials, connect,
                leftCallbacks, results, expectedResultCount);
    }
    //获得用户插件认证配置
    private boolean needAllPluginsToReturnTrue() {
        return this.hiveMQConfigurationService.internalConfiguration()
                .getBoolean(Internals.PLUGIN_AUTHENTICATION_NEED_ALL_PLUGINS_TO_RETURN_TRUE);
    }
    //判断是否认证通过
    private boolean accepted(List<PluginOnAuthenticationResult> results) {
        boolean needAllPluginsToReturnTrue = needAllPluginsToReturnTrue();
        for (PluginOnAuthenticationResult result : results) {
            if (!needAllPluginsToReturnTrue && result.isAuthenticated()) {
                return true;
            }
            if (needAllPluginsToReturnTrue && !result.isAuthenticated()) {
                return false;
            }
        }
        return needAllPluginsToReturnTrue;
    }

    //同步获得结果的FutureCallback
    @VisibleForTesting
    static class ResultCallback implements FutureCallback<PluginOnAuthenticationResult> {
        private final ChannelHandlerContext ctx;
        private final ClientCredentials clientCredentials;
        private final Connect connect;
        private final Queue<OnAuthenticationCallback> leftCallbacks;
        private final List<PluginOnAuthenticationResult> results;
        private final int expectedResultCount;

        public ResultCallback(ChannelHandlerContext ctx,
                              ClientCredentials clientCredentials,
                              Connect connect,
                              Queue<OnAuthenticationCallback> leftCallbacks,
                              List<PluginOnAuthenticationResult> results,
                              int expectedResultCount) {
            this.ctx = ctx;
            this.clientCredentials = clientCredentials;
            this.connect = connect;
            this.leftCallbacks = leftCallbacks;
            this.results = results;
            this.expectedResultCount = expectedResultCount;
        }

        //没有异常,回调,并触发一个插件认证完成事件
        @Override
        public void onSuccess(@Nullable PluginOnAuthenticationResult result) {
            this.results.add(result);
            this.ctx.pipeline().fireUserEventTriggered(
                    new PluginOnAuthenticationCallbackCompleted(this.leftCallbacks, this.results, this.connect,
                            this.clientCredentials, this.expectedResultCount));
        }
        //有异常,回调,并触发一个插件认证完成事件
        public void onFailure(Throwable t) {
            LOGGER.error("OnAuthenticationCallback failed. Skipping all other handlers");
            this.results.add(new PluginOnAuthenticationResult(false, ReturnCode.REFUSED_NOT_AUTHORIZED, true,
                    new AuthenticationException(t.getMessage() + " See log for more information", ReturnCode.REFUSED_NOT_AUTHORIZED)));
            this.ctx.pipeline().fireUserEventTriggered(
                    new PluginOnAuthenticationCallbackCompleted(this.leftCallbacks, this.results,
                            this.connect, this.clientCredentials, this.expectedResultCount));
        }
    }

    //认证task
    @VisibleForTesting
    static class Task implements CallableTask<PluginOnAuthenticationResult> {
        private final OnAuthenticationCallback callback;
        private final ClientCredentials clientCredentials;
        private final Metrics metrics;

        public Task(@NotNull OnAuthenticationCallback callback,
                    ClientCredentials clientCredentials,
                    Metrics metrics) {
            this.callback = callback;
            this.clientCredentials = clientCredentials;
            this.metrics = metrics;
        }

        @Override
        public PluginOnAuthenticationResult call() throws Exception {
            //获得埋点该插件执行时间的上下文
            Timer.Context timer = this.metrics.pluginTimerAuthentication().time();
            try {
                //调用callback,去认证
                Boolean authenticated = this.callback.checkCredentials(this.clientCredentials);
                //构建认证结果
                PluginOnAuthenticationResult result = new PluginOnAuthenticationResult(authenticated, authenticated ? ReturnCode.ACCEPTED : ReturnCode.REFUSED_NOT_AUTHORIZED, false);
                //停止插件执行时间计时
                timer.stop();
                return result;
            } catch (AuthenticationException e) {
                //当插件抛出认证失败exception
                LOGGER.debug("An exception was raised when calling the OnAuthenticationCallback {}:", this.callback.getClass(), e);
                //构建认证结果
                PluginOnAuthenticationResult result = new PluginOnAuthenticationResult(false, e.getReturnCode(), true, e);
                //停止计时
                timer.stop();
                return result;
            } catch (Throwable t) {
                //当插件抛出其他Throwable
                LOGGER.error("Unhandled Exception in OnAuthenticationCallback {}. Skipping all other handlers", this.callback.getClass());
                //停止计时
                timer.stop();
                //插件异常的处理器,去记录日志
                PluginExceptionUtils.log(t);
                //构建认证结果
                return new PluginOnAuthenticationResult(false, ReturnCode.REFUSED_NOT_AUTHORIZED, true,
                        new AuthenticationException(t.getMessage() + " See log for more information", ReturnCode.REFUSED_NOT_AUTHORIZED));
            }
        }

        @NotNull
        public Class callbackType() {
            return this.callback.getClass();
        }
    }
}

其他事件类

这几个类都是简单的pojo,在前面的源码注释中都已经描述过,所以就不写注释了。

public class PluginOnAuthentication {
    private final Connect connect;
    private final ClientCredentials clientCredentials;

    public PluginOnAuthentication(Connect connect, ClientCredentials clientCredentials) {
        this.connect = connect;
        this.clientCredentials = clientCredentials;
    }

    public Connect getConnect() {
        return connect;
    }

    public ClientCredentials getClientCredentials() {
        return clientCredentials;
    }
}

public class PluginOnAuthenticationCallbackCompleted {
    private final Queue<OnAuthenticationCallback> leftCallbacks;
    private final int expectedResultCount;
    private final List<PluginOnAuthenticationResult> results;
    private final Connect connect;
    private final ClientCredentials clientCredentials;

    public PluginOnAuthenticationCallbackCompleted(Queue<OnAuthenticationCallback> leftCallbacks,
                                                   List<PluginOnAuthenticationResult> results,
                                                   Connect connect,
                                                   ClientCredentials clientCredentials,
                                                   int expectedResultCount) {
        this.leftCallbacks = leftCallbacks;
        this.results = results;
        this.connect = connect;
        this.clientCredentials = clientCredentials;
        this.expectedResultCount = expectedResultCount;
    }

    public Queue<OnAuthenticationCallback> getLeftCallbacks() {
        return leftCallbacks;
    }

    public int getExpectedResultCount() {
        return expectedResultCount;
    }

    public List<PluginOnAuthenticationResult> getResults() {
        return results;
    }

    public Connect getConnect() {
        return connect;
    }

    public ClientCredentials getClientCredentials() {
        return clientCredentials;
    }
}

public class PluginOnAuthenticationCompleted {
    private final Connect connect;
    private final ClientCredentials clientCredentials;
    private final ReturnCode returnCode;
    private final AuthenticationException exception;

    public PluginOnAuthenticationCompleted(Connect connect,
                                           ClientCredentials clientCredentials,
                                           ReturnCode returnCode) {
        this.connect = connect;
        this.clientCredentials = clientCredentials;
        this.returnCode = returnCode;
        this.exception = null;
    }

    public PluginOnAuthenticationCompleted(Connect connect,
                                           ClientCredentials clientCredentials,
                                           ReturnCode returnCode,
                                           AuthenticationException exception) {
        this.connect = connect;
        this.clientCredentials = clientCredentials;
        this.returnCode = returnCode;
        this.exception = exception;
    }

    public Connect getConnect() {
        return connect;
    }

    public ClientCredentials getClientCredentials() {
        return clientCredentials;
    }

    public ReturnCode getReturnCode() {
        return returnCode;
    }

    public AuthenticationException getException() {
        return exception;
    }
}

MQTT交流群:221405150

RocketMQ交流群:10648794

NewSQL交流群:153575008


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容