vertx初探

404NotFound
• 阅读 11869

背景

vertx是一个我去年看完netty就一直想看看的工具,但因为拖延加懒,最近才看了看文档写了写demo, 算是对它有了一点点了解,之所以想写一点,其实是想自己给自己总结下vertx的一些核心的概念。

vertx core

vertx core 提供了一些 vertx的基本操作,如经常用到的

  1. 编写TCP客户端和服务器
  2. 编写HTTP客户端和服务器
  3. EventBus
  4. file操作
  5. HA
  6. 集群

先上一段代码看下vertx创建一个httpServer:

        //创建一个vertx实例
        VertxOptions vo = new VertxOptions();
        vo.setEventLoopPoolSize( 1);
        Vertx vertx = Vertx.vertx(vo);

        vertx.deployVerticle(MyFirstVerticle.class.getName()); 
        DeploymentOptions().setInstances(2)

        //MyFirstVerticle.java
        public class MyFirstVerticle extends AbstractVerticle {
        public void start() {

        vertx.createHttpServer().requestHandler(req -> {
            System.out.println(Thread.currentThread());
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            req.response()
                    .putHeader("content-type", "text/plain")
                    .end("Hello World!");
            this.deploymentID();
            Context c=vertx.getOrCreateContext();
        }).listen(8080);

    }
}

vertx实例是最核心的一个对象 是宁做几乎一切事情的基础,包括创建客户端和服务器、获取事件总线的引用、设置定时器等等。
是不是很想说一句,嗨,就这,不就nodejs吗

EventLoop

EventLoop算是vertx的基本模型了,简单的讲就是所有的操作都是以 触发 的方式来进行,将IO的操作完全交给vertx,开发者真正要关心的是IO各个阶段的 事件 ,讲的朴素一点就像是js的回调函数一样。
个人觉得vertx的EventLoop 基本等同于Netty的模型,如果真要探索起来,怕是要从Linux的多路复用,select函数,java的NIO,和netty一路将过来了,所以尝试用画图的方式来更形象的描绘:
vertx初探

其实EventLoop 就是一条线程,上面挂的每一个channel就是一个IO连接,底层利用的就是IO多路复用加select 或poll,epoll,来保证每一个线程可以保证控制很多个IO连接,而每一个连接都会挂一个handler,来处理这个连接的 每一个事件 例如:init,connected,read.writer,close。
这个模型的左半部分同样适用于netty。右半部分有一个workPool是一个线程池,是vertx新增的东西,是用来专门处理耗时操作的,如 file,阻塞的DB操作,为什么要加这个概念哪,因为不要阻塞EventLoop是NIO的基本守则。
阻塞操作操作代码如下:

        executor.executeBlocking(future -> {
            System.out.println("Action Thread"+Thread.currentThread());
            // 调用一些需要耗费显著执行时间返回结果的阻塞式API
            String result = blockingMethod("hello");
            future.complete(result);
        }, res -> {
            System.out.println("Handler Thread"+Thread.currentThread());
            System.out.println("The result is: " + res.result());
            executor.close();
        });

verticle

verticle 其实一直是让我困惑的一个概念,因为vertx的主要运行,基本都是围绕vertx实例来进行的,后面我为verticle找到了一个合理的角色,叫他为vertx实例的一个发布单元,什么是一个 发布单元哪,举个例子,一个HttpServer的发布是一个发布单元。
verticle具有以下几个特点:

  1. 每个verticle占用一个EventLoop线程,且只对应一个EventLoop
  2. 每个verticle中创建的HttpServer,EventBus等等,都会在这个verticle回收时同步回收
  3. 在多个verticle中创建同样端口的httpServer,不会有错误,会变成两个EventLoop线程处理同一个HttpServer的连接,所以多核机器,肯定是需要设置多个verticle的实例来加强性能的。

可以这么想,vertx实例就是一台服务器,而verticle就是上面跑的进程。

EventBus

EventBus 是沟通verticle的桥梁,且可沟通集群中不同vertx实例的verticle,操作很简单。这个似乎概念很简单,就是队列呗,上段代码看看:

            //创建一个EventBus
            EventBus eb = vertx.eventBus();

            req.bodyHandler(totalBuffer -> {
                eb.send("news.uk.sport", totalBuffer.toString("UTF-8"));
            });

            //消费
            EventBus eb = vertx.eventBus();
            eb.consumer("news.uk.sport", message -> {
            System.out.println("前台传入的内容:" + message.body()+""+this);
            });

模型如图:
vertx初探

vertx web

vertx core已经提供了基本的HttpServer的操作,但实际上功能远远不够正常的开发,所以vertx web作为一个拓展包,是web开发必须的。他提供了一个基本概念router,来进行各种匹配,使得请求能进入正确的handler,其实就是有点springMvc的各种路径匹配。使用起来代码:

        HttpServer server = vertx.createHttpServer();
 

        Router router = Router.router(vertx);

        router.route("/some/path/").handler(routingContext -> {

            HttpServerResponse response = routingContext.response();
            // 如果不能一次输出所有的response,就需要设置为true
            response.setChunked(true);

            response.write("route1\n");
 
        });
        server.requestHandler(router).listen(8080);

web包括的功能很多,有各种匹配,content-type匹配,路径规则匹配,CROS,错误处理,文件传输 等等

vertx fileSystem

vretx中的文件操作主要采用了javaNio包中的文件操作,通过看源码我们可以发现文件操作也是运行在workPool中的,看下代码:

            fs.copy("C:\\Users\\Administrator\\Desktop\\Untitled-4.txt", "C:\\Users\\Administrator\\Desktop\\ss.txt", res -> {
                System.out.println("file copy handle" + System.currentTimeMillis());
                System.out.println("file copy handle" + Thread.currentThread());
                if (res.succeeded()) {
                    System.out.println("success");
                } else {
                    System.out.println("error");
                }
            })

源码:

    //FileSystemImpl.java

    /**
     * Run the blocking action using a thread from the worker pool.
     */
    public void run() {
      context.executeBlockingInternal(this, handler);
    }

vertx DB

vertx其实提供了 两种数据库操作。一种是正常的jdbc操作,一种是异步非阻塞的数据库操作,但是只限于PG和mysql。

jdbc操作

看一段代码:

JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
                .put("url", "jdbc:postgresql://localhost:5432/postgres")
                .put("driver_class", "org.postgresql.Driver")
                .put("max_pool_size", 30).put("user","postgres").put("password","postgres"));

client.getConnection(res -> {
                if (res.succeeded()) {
                    SQLConnection connection = res.result();
                    
                    connection.query("SELECT * FROM userb", res2 -> {
                        if (res2.succeeded()) {
                            ResultSet rs = res2.result();
                            System.out.println(rs.toJson());
                        }
                    });
                } else {
                    System.out.println("连接失败");                }
            });

点进去发现:其实做的还是阻塞操作,

    //AbstractJDBCAction.java
    public void execute(Connection conn, TaskQueue statementsQueue, Handler<AsyncResult<T>> resultHandler) {
        this.ctx.executeBlocking((future) -> {
            this.handle(conn, future);
        }, statementsQueue, resultHandler);
    }

异步数据库操作

这我一直很好奇的,因为jdbc在天然上就是阻塞的,所以要想做到数据库的异步,就得放弃厂商提供的driver,自己做一套编解码,看下代码:

PgConnectOptions connectOptions = new PgConnectOptions()
                .setPort(5432)
                .setHost("localhost")
                .setDatabase("postgres")
                .setUser("postgres")
                .setPassword("postgres");

        // Pool options
        PoolOptions poolOptions = new PoolOptions()
                .setMaxSize(5);

        // Create the client pool
        PgPool client = PgPool.pool(vertx, connectOptions, poolOptions);
    
    client.query("SELECT * FROM  userb ", ar -> {
                if (ar.succeeded()) {
                    RowSet<Row> result = ar.result();
                    result.forEach((r) -> response.write((String)r.getValue("name")));
                    response.end();

                } else {
                    System.out.println("Failure: " + ar.cause().getMessage();
                }
            });

这样就能做到数据库的异步操作,且可以包含事务的控制。简直丝滑

关于数据库异步的思考

数据库异步看起来十分黑科技,但是我们应该明白的是,异步非阻塞的NIO最大的优点就在于利用很少的线程来控制更多的IO连接,这使得在超多连接,IO密集的操作中有更好的表现,但是数据库连接本来一个java系统就不会占用几个,记得以前看过一个文章讲连接数设置最好=CPUcore2磁盘数,那么数据库的NIO在一般的业务系统中,似乎并不必要。
插一个知乎上大佬的回答:

https://www.zhihu.com/questio...

vertx模型图

vertx初探
简单明了

vertx的缺点

说到缺点,明显就是回调地狱了,主要还是看过 python的协程,所以难免拿来做个比较,

摘抄自廖雪峰的python教程
import asyncio

from aiohttp import web

async def index(request):
    await asyncio.sleep(0.5)
    return web.Response(body=b'<h1>Index</h1>')

async def hello(request):
    await asyncio.sleep(0.5)
    text = '<h1>hello, %s!</h1>' % request.match_info['name']
    return web.Response(body=text.encode('utf-8'))

async def init(loop):
    app = web.Application(loop=loop)
    app.router.add_route('GET', '/', index)
    app.router.add_route('GET', '/hello/{name}', hello)
    srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
    print('Server started at http://127.0.0.1:8000...')
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

可以看到协程 可以将本该做回调的response handler变成了更符合编程习惯的方式,还是比较舒服的。

讲点废话

本来还想把demo放到github上来给像我一样懒的人直接clone下来跑的,但是确实都是官网的例子,放上去太羞耻了。
这文章写的很纠结,写详细了东西太多,写总结性的东西吧,太抽象,没看过的觉得一脸懵,懂得觉得写的没啥意义。
就这吧,如果有大佬想和我讨论或是哪里写的不对,请积极发炎。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
4年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Easter79 Easter79
4年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
4年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
4年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这