vertx是一个我去年看完netty就一直想看看的工具,但由于拖延加懒,最近才看了看文档写了写demo, 算是对它有了一点点了解,之因此想写一点,实际上是想本身给本身总结下vertx的一些核心的概念。java
vertx core 提供了一些 vertx的基本操做,如常常用到的node
先上一段代码看下vertx建立一个httpServer:python
//建立一个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吗mysql
EventLoop算是vertx的基本模型了,简单的讲就是全部的操做都是以 触发 的方式来进行,将IO的操做彻底交给vertx,开发者真正要关心的是IO各个阶段的 事件 ,讲的朴素一点就像是js的回调函数同样。
我的以为vertx的EventLoop 基本等同于Netty的模型,若是真要探索起来,怕是要从Linux的多路复用,select函数,java的NIO,和netty一路将过来了,因此尝试用画图的方式来更形象的描绘:git
其实EventLoop 就是一条线程,上面挂的每个channel就是一个IO链接,底层利用的就是IO多路复用加select 或poll,epoll,来保证每个线程能够保证控制不少个IO链接,而每个链接都会挂一个handler,来处理这个链接的 每个事件 例如:init,connected,read.writer,close。
这个模型的左半部分一样适用于netty。右半部分有一个workPool是一个线程池,是vertx新增的东西,是用来专门处理耗时操做的,如 file,阻塞的DB操做,为何要加这个概念哪,由于不要阻塞EventLoop是NIO的基本守则。
阻塞操做操做代码以下:github
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 其实一直是让我困惑的一个概念,由于vertx的主要运行,基本都是围绕vertx实例来进行的,后面我为verticle找到了一个合理的角色,叫他为vertx实例的一个发布单元,什么是一个 发布单元哪,举个例子,一个HttpServer的发布是一个发布单元。
verticle具备如下几个特色:web
能够这么想,vertx实例就是一台服务器,而verticle就是上面跑的进程。spring
EventBus 是沟通verticle的桥梁,且可沟通集群中不一样vertx实例的verticle,操做很简单。这个彷佛概念很简单,就是队列呗,上段代码看看:sql
//建立一个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 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,错误处理,文件传输 等等
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其实提供了 两种数据库操做。一种是正常的jdbc操做,一种是异步非阻塞的数据库操做,可是只限于PG和mysql。
看一段代码:
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...
简单明了
说到缺点,明显就是回调地狱了,主要仍是看过 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下来跑的,可是确实都是官网的例子,放上去太羞耻了。 这文章写的很纠结,写详细了东西太多,写总结性的东西吧,太抽象,没看过的以为一脸懵,懂得以为写的没啥意义。 就这吧,若是有大佬想和我讨论或是哪里写的不对,请积极发炎。