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下來跑的,可是確實都是官網的例子,放上去太羞恥了。 這文章寫的很糾結,寫詳細了東西太多,寫總結性的東西吧,太抽象,沒看過的以爲一臉懵,懂得以爲寫的沒啥意義。 就這吧,若是有大佬想和我討論或是哪裏寫的不對,請積極發炎。