Netty组件集成到Vert.x

2,348 阅读2分钟

简单粗暴地将原有Netty组件集成到Vert.x的办法

前情提要

Vert.x写的很爽,但是那个vertx.createNetServer()获取到的tcp服务器中还是需要自己处理“粘包”,“半包”(这里只做比喻用,就是如何处理数据边界问题),而且更重要的是无法复用Netty库或者旧有的codec

为了解决这个问题我根据vertx.createNetServer()的源码,自己从ServerBootstrap开始造,挺费劲的

在和Vertx中国用户组的群友们吹水的时候,有人提到可以进行一个类型强转获取到Netty的pipeline,让我去参考io.vertx.mysqlclient.impl.MySQLSocketConnection#init()这个方法

实战

版本

       <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-core</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>io.vertx</groupId>
            <artifactId>vertx-mysql-client</artifactId>
         <version>4.0.0</version>

代码

vertx.createNetServer(netServerOptions)
                .connectHandler(socket -> {
                    ChannelPipeline pipeline = ((NetSocketInternal) socket).channelHandlerContext().pipeline();

                })
                .listen(port)
                .map(netServer -> {
                    setNetServer(netServer);
                    return this;
                });

嗯,就这么简单。。。。。。

原理

前置知识

Http(3.0除外),MySQL,Redis等协议都是基于TCP协议的,也就说Vertx下面的这些Client也是基于Netty的

为什么不可能是基于Vertx本体的?因为那个api真的很难用。

所以只要去看看其Client的源码就知道了

源码

这里选取MySQL的Client查看,直接查找pipeline

@Override
  public void init() {
    //....无关代码已经省略
    ChannelPipeline pipeline = socket.channelHandlerContext().pipeline();
    //....无关代码已经省略
  }

然后再去看看socket是个什么东西,是其父类io.vertx.sqlclient.impl.SocketConnectionBase

中的一个字段,其类型为io.vertx.core.net.impl.NetSocketInternal,再反查他是在哪里初始化的

public SocketConnectionBase(NetSocketInternal socket,
                              boolean cachePreparedStatements,
                              int preparedStatementCacheSize,
                              Predicate<String> preparedStatementCacheSqlFilter,
                              int pipeliningLimit,
                              ContextInternal context) {
    this.socket = socket;
    //....无关代码已经省略
  }

在反查其使用,看看这个NetSocketInternal是怎么传入的,经过多次跳转可以看到在这里io.vertx.mysqlclient.impl.MySQLConnectionFactory#doConnectInternal的代码告诉我到底传入了什么,嗯,果然是类型强转来的

@Override
  protected void doConnectInternal(Promise<Connection> promise) {
      //netClient类型 NetClient netClient;
    Future<NetSocket> fut = netClient.connect(socketAddress);
    fut.onComplete(ar -> {
      if (ar.succeeded()) {
        NetSocket so = ar.result();
        MySQLSocketConnection conn = new MySQLSocketConnection((NetSocketInternal) so, cachePreparedStatements, preparedStatementCacheSize, preparedStatementCacheSqlFilter, context);
        conn.init();
        conn.sendStartupMessage(username, password, database, collation, serverRsaPublicKey, properties, sslMode, initialCapabilitiesFlags, charsetEncoding, authenticationPlugin, promise);
      } else {
        promise.fail(ar.cause());
      }
    });
  }

预告

上一个强行获取Eventloop线程做的嵌入,写都写了,过两天也写写吧