国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

次時代Java編程(一):續 vertx-sync實踐

vpants / 1342人閱讀

摘要:定時器例子之前通過調用定時器,需要傳一個回調,然后所有的代碼邏輯都包在里面。這里定時器會阻塞在這一行,直到一秒后才會執行下面的一行。

之前介紹過quasar,如果你希望在vert.x項目里使用coroutine的話,建議使用vertx-sync。本篇將介紹vertx-sync。

本來打算另起一篇,寫其他方面的東西,但是最近比較忙,就先寫一篇實踐方面的文章。

vertx-sync是什么

上一篇我們已經講了 Fiber 相關的知識,想必大家對Java實現類似Golang的coroutine已經有印象了,既然Java世界里有第三方提供了這么好的庫,那我們就看看怎么跟 vert.x 結合起來使用。

vert.x官方為了解決異步代碼編寫的困難,使之更加同步化對開發人員更友好,便基于quasar包裝了一個的同步庫,vertx-sync,該庫的作者同樣也是vert.x的原作者,所以完成度還是很高的。

vertx-sync 對外只是暴露了幾個簡單的靜態API,來完成對vert.x體系內一系列的操作包裝,其實主要也就是三靜態API而已。

Sync.fiberHandler 如果你希望你的handler里有一些邏輯需要在Fiber里運行,則你的handler必須用這個方法包一下。

Sync.awaitEvent 從Handler里返回一個事件結果(同步的),且 不會阻塞EventLoop

Sync.awaitResult 從Handler里返回一個異步事件結果(同步的),且 不會阻塞EventLoop

直接看介紹可能有點不直觀,下面跑幾個例子。

使用vertx-sync

之前介紹過quasar,如果你希望在項目里使用coroutine的話,需要在JVM里設置一個參數,用于應用啟動前修改字節碼(注入一些中斷方法),從而可以達到協程的目的。

具體方法也很簡單。

-javaagent:/path/to/the/quasar-core-0.7.5-jdk8.jar

如果是基于Maven跑單元測試,那只需要引用quasar instrument的插件就可以里


    com.vlkan
    quasar-maven-plugin
    0.7.3
    
        true
        true
        true
    
    
        
            
                instrument
            
        
    
    
        
            co.paralleluniverse
            quasar-core
            0.7.5
        
    

上面是一些非常必要的準備工作,否則你無法使用quasar以及vertx-sync。

vertx定時器例子

之前通過vert.x調用定時器,需要傳一個回調handler,然后所有的代碼邏輯都包在里面。

vertx.setTimer(1000L, h -> {
    System.out.println("time"s up");
});

現在我們來重新塑造一下三觀。

awaitEvent(h -> vertx.setTimer(1000L, h));
System.out.println("time"s up");

這里定時器會阻塞在awaitEvent這一行,直到一秒后才會執行下面的一行。有點類似執行 Thread.sleep(1000L),但是并不會阻塞 EventLoop 因為quasar會在EventLoop基礎之上再開啟一個fiber。

下面我看個稍微復雜點的例子。

HTTP Client請求例子

我們先用傳統的回調方式使用vert.x的HttpClient API。

HttpClientRequest httpClientRequest = vertx.createHttpClient().get("leapcloud.cn");
httpClientRequest.handler(response -> {
    response.handler(responseBody -> {
        System.out.println(responseBody.toString());
    });
}).end();

這里有兩層回調嵌套,一層是得到Http的Response,另一層是從Response里得到詳細的body。因為有lambda表達式才使得Java現在看起來并不是那么惡心。但是如果我們需要根據body的內容進一步做判斷去繼續請求其他頁面,則嵌套會變的非常的深。下面嘗試改造成sync方式看看。

HttpClientRequest httpClientRequest = vertx.createHttpClient().get("leapcloud.cn");
HttpClientResponse response = awaitEvent(Sync::fiberHandler);
Buffer body = awaitEvent(response::handler);
System.out.println(body.toString());

額,是不是感覺看著很舒服,無嵌套,直接順序下來,非常的直觀,加上Java8特有的方法引用,會讓代碼更精簡。

通過vertx-sync使用Vert.x JDBC

寫過vert.x同學肯定知道其vertx-jdbc-client為了使其兼容異步開發模型,將JDBC的底層線程池用異步方式包裝了一下,也就是說JDBC層還是通過線程池去訪問數據庫的,但是是通過vert.x的context做了層封裝,使其可以將結果放到對應的 EventLoop 里,這樣比較符合vert.x的開發風格。但是帶來的弊端就是嵌套太深。

final JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")
.put("max_pool_size", 30));

client.getConnection(conn -> {
    if (conn.failed()) {
        System.err.println(conn.cause().getMessage());
        return;
    }

    final SQLConnection connection = conn.result();
    connection.execute("create table test(id int primary key, name varchar(255))", res -> {
        if (res.failed()) {
            throw new RuntimeException(res.cause());
        }
        // insert some test data
        connection.execute("insert into test values(1, "Hello")", insert -> {
            // query some data
            connection.query("select * from test", rs -> {
                for (JsonArray line : rs.result().getResults()) {
                    System.out.println(line.encode());
                }

                // and close the connection
                connection.close(done -> {
                    if (done.failed()) {
                        throw new RuntimeException(done.cause());
                    }
                });
            });
        });
    });
});

上面代碼可以是不是有點惡心呢?嘗試改造一下吧。

final JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")
.put("max_pool_size", 30));

try (SQLConnection conn = awaitResult(jdbc::getConnection)) {
    awaitResult(h -> conn.execute("create table test(id int primary key, name varchar(255))", h));
    awaitResult(h -> conn.execute("insert into test values(1, "Hello")", h));
    ResultSet query = awaitResult(h -> conn.query("select * from test", h));
    for (JsonArray line : query.result.getResults()) {
        System.out.println(line.encode());
    }
    AsyncResult done = awaitResult(h -> conn.close(h));
    if (done.failed()) {
        throw new RuntimeException(done.cause())
    }
} catch (Exception e) {
    e.printStackTrace();
}

除了一個try catch,其他都沒有嵌套,整體邏輯的可讀性非常高,完全是線性的。

如何將邏輯放倒Fiber里

你也許會發現我們似乎一直都沒有用到 fiberHandler 這個靜態方法,上面雖然寫了定義,可能大家還是不能夠理解,這里結合場景也許能更好理解。

我們嘗試實現一個操作很耗時的邏輯然后包到fiber里,避免 EventLoop 被阻塞。這里你也許會很好奇,既然 Fiber 這么廉價開啟10萬8萬的無所謂啊,恩,這里再提一下quasar的重點部分: fiber可以很廉價的被創造出來,但是他本質上還是跑在一個線程上面,如果其中一個fiber執行了非常耗時的操作,則后面的fiber會一直等待,從而造成整個線程阻塞。 也就是說一個fiber不能執行非常耗時的操作,比如計算100萬以內的素數之和,對于這種操作,我們可以通過直接將邏輯放到vert.x的worker線程里多帶帶去跑,然后通過fiber包裝一下就可以了。

AsyncResult result = awaitResult(fiberHandler(h -> vertx.executeBlocking((Handler>) event -> {
    //求百萬以內素數之和,這里的邏輯會在vert.x的worker線程里跑。隨便耗時多久,都不會阻塞EventLoop
    long sum = sumOfPrime(1, 000, 000);
    event.complete(sum);
}, h)));
//打印結果
System.out.println(result.result());

這里你會注意到 awaitReslt 里用了 fiberHandler ,因為executeBlocking里的 handler 邏輯本身并沒有跑在fiber體系下,所以會導致無效,而fiberHandler的作用就是將一段vert.x的handler包到 fiber 里。使之后續的await可以將其結果返回,這里使用awaitResult返回結果。
我們再深入一點看看 fiberHandler 方法里到底干了什么。

@Suspendable
public static  Handler fiberHandler(Handler handler) {
  FiberScheduler scheduler = getContextScheduler();
  return p -> new Fiber(scheduler, () -> handler.handle(p)).start();
}

這里獲取Fiber的調度器,然后直接new了一個 Fiber ,避免了我們自己對邏輯做Fiber包裝。是不是很簡單呢。

總結

相比較傳統的回調Handler,vertx-sync的包裝十分優雅,基本可以恢復到同步方法調用級別,很好的減輕了異步回調帶來的心智負擔。

但是這個畢竟不是JVM級別的實現,所以或多或少還是有點門檻的,比如部署的時候,需要通過設置JVM參數來修改部分字節碼,同時還要注意一些需要掛起的方法上面加注釋或者強行讓其拋出可中斷異常。個人建議在一些不重要的工具級項目里使用,非常重要的項目不推薦使用,當然了如果你覺得你的業務只需要依賴vert.x那么強烈你推薦你使用,只要記得打開 BlockingChecker 就好,可以即時的發現潛在的阻塞邏輯。

責編:另外7.24號,我們力譜宿云攜手Vert.x中國用戶組在太庫·上海的贊助下舉辦了一場關于Vert.x的技術,之后還會有系列活動,有興趣的同學們可以關注我們的微信公眾號:MaxLeap_yidongyanfa,了解更多資訊。

作者往期佳作
次時代Java編程(一) Java里的協程

作者信息
本文系力譜宿云 LeapCloud旗下MaxLeap團隊_UX成員:劉小溪 【原創】
力譜宿云首發地址:https://blog.maxleap.cn/archi...
劉小溪,Maxleap的高級開發工程師,喜歡倒騰一些有意思的技術框架,對新的技術以及語言非常有興趣,以前在shopex擔任架構師,目前在Maxleap負責基礎架構以及服務框架這塊技術,同時也會對Vert.x的社區提供一些開源上的支持。

關于MaxLeap
MaxLeap 是力譜宿云推出,為移動應用開發、運營提供一站式后端云服務, 包括應用開發所需的后端云數據庫、云數據源、云代碼、云容器、 IM、移動支付、應用內社交、第三方登錄、社交分享等基礎服務,以及針對應用運營的數據分析、推送營銷,用戶支持等服務, 覆蓋移動應用的研發、運營完整生命周期。
MaxLeap 致力于讓移動應用開發運營更快速簡單。

官網:https://maxleap.cn/
歡迎掃二維碼,關注我們

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/64975.html

相關文章

  • 如何用java 5分鐘實現個最簡單的mysql代理服務器?

    摘要:用基于快速實現一個最簡單的代理服務器,只需要分鐘時間。監控統計客戶端的請求情況,請求分布統計請求類型等,以此來優化數據庫的使用。 用java8基于vert.x3 快速實現一個最簡單的mysql代理服務器,只需要5分鐘時間。 showImg(/img/bVz0vh); 什么是mysql 代理? mysql代理是介于client端和mysql服務端中間層服務,如下圖所示: showImg(...

    JerryZou 評論0 收藏0
  • Vert.x 3.4.x 之 Web Client

    摘要:為此我們可以使用來發送一個比如流,會自動為我們實現管道傳輸數據。因為流的長度不確定,請求將使用即分塊傳輸。方法可以被多次安全的調用,這對于無數據的請求很容易復用配置和。如此在接受響應的回調函數里可以直接得到設置的響應解碼體。 本文主要介紹Vert.x 3.4.x 版本新組件Web Client的使用 showImg(https://segmentfault.com/img/bVL8iX...

    selfimpr 評論0 收藏0
  • 后端文章 - 收藏集 - 掘金

    摘要:為什么我會說它們是一樣的簡單思考一下我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發與架構。一方案調研版本選擇當前主流版本是和應用的后臺運行配置后端掘金醬油一篇,整理一下關于后臺運行的一些配置方式。 分享 50 個完整的 React Native 項目 - 掘金本文為 Marno 原創,轉載必須保留出處! 公眾號 aMarno,關注后回復 RN 加入交流群 簡書專題《 Rea...

    CntChen 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<