亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

次時(shí)代Java編程(一) Java里的協(xié)程 | 出續(xù)篇 更新ed

gecko23 / 2631人閱讀

摘要:所以系統(tǒng)的吞吐能力取決于每個(gè)線程的操作耗時(shí)。另外過多的線程,也會(huì)帶來更多的開銷。其代表派是以及里的新秀。類似線程也有自己的棧。線程中斷的條件只有兩個(gè),一個(gè)是拋異常,另外一個(gè)就是。

什么是協(xié)程(coroutine)

這東西其實(shí)有很多名詞,比如有的人喜歡稱為纖程(Fiber),或者綠色線程(GreenThread)。其實(shí)最直觀的解釋可以定義為線程的線程。有點(diǎn)拗口,但本質(zhì)上就是這樣。

我們先回憶一下線程的定義,操作系統(tǒng)產(chǎn)生一個(gè)進(jìn)程,進(jìn)程再產(chǎn)生若干個(gè)線程并行的處理邏輯,線程的切換由操作系統(tǒng)負(fù)責(zé)調(diào)度。傳統(tǒng)語言C++ Java等線程其實(shí)與操作系統(tǒng)線程是1:1的關(guān)系,每個(gè)線程都有自己的Stack, Java在64位系統(tǒng)默認(rèn)Stack大小是1024KB,所以指望一個(gè)進(jìn)程開啟上萬個(gè)線程是不現(xiàn)實(shí)的。但是實(shí)際上我們也不會(huì)這么干,因?yàn)槠疬@么多線程并不能充分的利用CPU,大部分線程處于等待狀態(tài),CPU也沒有這么核讓線程使用。所以一般線程數(shù)目都是CPU的核數(shù)。

傳統(tǒng)的J2EE系統(tǒng)都是基于每個(gè)請(qǐng)求占用一個(gè)線程去完成完整的業(yè)務(wù)邏輯,(包括事務(wù))。所以系統(tǒng)的吞吐能力取決于每個(gè)線程的操作耗時(shí)。如果遇到很耗時(shí)的I/O行為,則整個(gè)系統(tǒng)的吞吐立刻下降,比如JDBC是同步阻塞的,這也是為什么很多人都說數(shù)據(jù)庫(kù)是瓶頸的原因。這里的耗時(shí)其實(shí)是讓CPU一直在等待I/O返回,說白了線程根本沒有利用CPU去做運(yùn)算,而是處于空轉(zhuǎn)狀態(tài)。暴殄天物啊。另外過多的線程,也會(huì)帶來更多的ContextSwitch開銷。

Java的JDK里有封裝很好的ThreadPool,可以用來管理大量的線程生命周期,但是本質(zhì)上還是不能很好的解決線程數(shù)量的問題,以及線程空轉(zhuǎn)占用CPU資源的問題。

先階段行業(yè)里的比較流行的解決方案之一就是單線程加上異步回調(diào)。其代表派是node.js以及Java里的新秀Vert.x。他們的核心思想是一樣的,遇到需要進(jìn)行I/O操作的地方,就直接讓出CPU資源,然后注冊(cè)一個(gè)回調(diào)函數(shù),其他邏輯則繼續(xù)往下走,I/O結(jié)束后帶著結(jié)果向事件隊(duì)列里插入執(zhí)行結(jié)果,然后由事件調(diào)度器調(diào)度回調(diào)函數(shù),傳入結(jié)果。這時(shí)候執(zhí)行的地方可能就不是你原來的代碼區(qū)塊了,具體表現(xiàn)在代碼層面上,你會(huì)發(fā)現(xiàn)你的局部變量全部丟失,畢竟相關(guān)的棧已經(jīng)被覆蓋了,所以為了保存之前的棧上數(shù)據(jù),你要么選擇帶著一起放入回調(diào)函數(shù)里,要么就不停的嵌套,從而引起反人類的Callback hell.

因此相關(guān)的Promise,CompletableFuture等技術(shù)都是為解決相關(guān)的問題而產(chǎn)生的。但是本質(zhì)上還是不能解決業(yè)務(wù)邏輯的割裂。

說了這么多,終于可以提一下協(xié)程了,協(xié)程的本質(zhì)上其實(shí)還是和上面的方法一樣,只不過他的核心點(diǎn)在于調(diào)度那塊由他來負(fù)責(zé)解決,遇到阻塞操作,立刻yield掉,并且記錄當(dāng)前棧上的數(shù)據(jù),阻塞完后立刻再找一個(gè)線程恢復(fù)棧并把阻塞的結(jié)果放到這個(gè)線程上去跑,這樣看上去好像跟寫同步代碼沒有任何差別,這整個(gè)流程可以稱為coroutine,而跑在由coroutine負(fù)責(zé)調(diào)度的線程稱為Fiber。比如Golang里的 go關(guān)鍵字其實(shí)就是負(fù)責(zé)開啟一個(gè)Fiber,讓func邏輯跑在上面。而這一切都是發(fā)生的用戶態(tài)上,沒有發(fā)生在內(nèi)核態(tài)上,也就是說沒有ContextSwitch上的開銷。

既然我們的標(biāo)題叫Java里的協(xié)程,自然我們會(huì)討論JVM上的實(shí)現(xiàn),JVM上早期有kilim以及現(xiàn)在比較成熟的Quasar。而本文章會(huì)全部基于Quasar,因?yàn)?b>kilim已經(jīng)很久不更新了。

簡(jiǎn)單的例子,用Java寫出Golang的味道

上面已經(jīng)說明了什么是Fiber,什么是coroutine。這里嘗試通過Quasar來實(shí)現(xiàn)類似于golang的coroutine以及channel。這里假設(shè)各位已經(jīng)大致了解golang。

為了對(duì)比,這里先用golang實(shí)現(xiàn)一個(gè)對(duì)于10以內(nèi)自然數(shù)分別求平方的例子,當(dāng)然了可以直接單線程for循環(huán)就完事了,但是為了凸顯coroutine的高逼格,我們還是要稍微復(fù)雜化一點(diǎn)的。

func counter(out chan<- int) {
  for x := 0; x < 10; x++ {
    out <- x
  }
  close(out)
}

func squarer(out chan<- int, in <-chan int) {
  for v := range in {
    out <- v * v
  }
  close(out)
}

func printer(in <-chan int) {
  for v := range in {
    fmt.Println(v)
  }
}

func main() {
  //定義兩個(gè)int類型的channel
  naturals := make(chan int)
  squares := make(chan int)

  //產(chǎn)生兩個(gè)Fiber,用go關(guān)鍵字
  go counter(naturals)
  go squarer(squares, naturals)
  //獲取計(jì)算結(jié)果
  printer(squares)
}

上面的例子,有點(diǎn)類似生產(chǎn)消費(fèi)者模式,通過channel兩解耦兩邊的數(shù)據(jù)共享。大家可以將channel理解為Java里的SynchronousQueue。那傳統(tǒng)的基于線程模型的Java實(shí)現(xiàn)方式,想必大家都知道怎么做,這里就不啰嗦了,我直接上Quasar版的,幾乎可以原封不動(dòng)的copy golang的代碼。

public class Example {

  private static void printer(Channel in) throws SuspendExecution,  InterruptedException {
    Integer v;
    while ((v = in.receive()) != null) {
      System.out.println(v);
    }
  }

  public static void main(String[] args) throws ExecutionException, InterruptedException, SuspendExecution {
    //定義兩個(gè)Channel
    Channel naturals = Channels.newChannel(-1);
    Channel squares = Channels.newChannel(-1);

    //運(yùn)行兩個(gè)Fiber實(shí)現(xiàn).
    new Fiber(() -> {
      for (int i = 0; i < 10; i++)
        naturals.send(i);
      naturals.close();
    }).start();

    new Fiber(() -> {
      Integer v;
      while ((v = naturals.receive()) != null)
        squares.send(v * v);
      squares.close();
    }).start();

    printer(squares);
  }
}

看起來Java似乎要啰嗦一點(diǎn),沒辦法這是Java的風(fēng)格,而且畢竟不是語言上支持coroutine,是通過第三方的庫(kù)。到后面我會(huì)考慮用其他JVM上的語言去實(shí)現(xiàn),這樣會(huì)顯得更精簡(jiǎn)一點(diǎn)。

說到這里各位肯定對(duì)Fiber很好奇了。也許你會(huì)表示懷疑Fiber是不是如上面所描述的那樣,下面我們嘗試用Quasar建立一百萬個(gè)Fiber,看看內(nèi)存占用多少,我先嘗試了創(chuàng)建百萬個(gè)Thread。

for (int i = 0; i < 1_000_000; i++) {
  new Thread(() -> {
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }).start();
}

很不幸,直接報(bào)Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread,這是情理之中的。下面是通過Quasar建立百萬個(gè)Fiber

public static void main(String[] args) throws ExecutionException, InterruptedException, SuspendExecution {
  int FiberNumber = 1_000_000;
  CountDownLatch latch = new CountDownLatch(1);
  AtomicInteger counter = new AtomicInteger(0);

  for (int i = 0; i < FiberNumber; i++) {
    new Fiber(() -> {
      counter.incrementAndGet();
      if (counter.get() == FiberNumber) {
        System.out.println("done");
      }
      Strand.sleep(1000000);
    }).start();
  }
  latch.await();
}

我這里加了latch,阻止程序跑完就關(guān)閉,Strand.sleep其實(shí)跟Thread.sleep一樣,只是這里針對(duì)的是Fiber

最終控制臺(tái)是可以輸出done的,說明程序已經(jīng)創(chuàng)建了百萬個(gè)Fiber,設(shè)置Sleep是為了讓Fiber一直運(yùn)行,從而方便計(jì)算內(nèi)存占用。官方宣稱一個(gè)空閑的Fiber大約占用400Byte,那這里應(yīng)該是占用400MB堆內(nèi)存,但是這里通過jmap -heap pid顯示大約占用了1000MB,也就是說一個(gè)Fiber占用1KB。

Quasar是怎么實(shí)現(xiàn)Fiber的

其實(shí)Quasar實(shí)現(xiàn)的coroutine的方式與Golang很像,只不過一個(gè)是框架級(jí)別實(shí)現(xiàn),一個(gè)是語言內(nèi)置機(jī)制而已。

如果你熟悉了Golang的調(diào)度機(jī)制,那理解Quasar的調(diào)度機(jī)制就會(huì)簡(jiǎn)單很多,因?yàn)閮烧呤遣畈欢嗟摹?/p>

Quasar里的Fiber其實(shí)是一個(gè)continuation,他可以被Quasar定義的scheduler調(diào)度,一個(gè)continuation記錄著運(yùn)行實(shí)例的狀態(tài),而且會(huì)被隨時(shí)中斷,并且也會(huì)隨后在他被中斷的地方恢復(fù)。Quasar其實(shí)是通過修改bytecode來達(dá)到這個(gè)目的,所以運(yùn)行Quasar程序的時(shí)候,你需要先通過java-agent在運(yùn)行時(shí)修改你的代碼,當(dāng)然也可以在編譯期間這么干。golang的內(nèi)置了自己的調(diào)度器,Quasar則默認(rèn)使用ForkJoinPool這個(gè)JDK7以后才有的,具有work-stealing功能的線程池來當(dāng)調(diào)度器。work-stealing非常重要,因?yàn)槟悴磺宄膫€(gè)Fiber會(huì)先執(zhí)行完,而work-stealing可以動(dòng)態(tài)的從其他的等等隊(duì)列偷一個(gè)context過來,這樣可以最大化使用CPU資源。

那這里你會(huì)問了,Quasar怎么知道修改哪些字節(jié)碼呢,其實(shí)也很簡(jiǎn)單,Quasar會(huì)通過java-agent在運(yùn)行時(shí)掃描哪些方法是可以中斷的,同時(shí)會(huì)在方法被調(diào)用前和調(diào)度后的方法內(nèi)插入一些continuation邏輯,如果你在方法上定義了@Suspendable注解,那Quasar會(huì)對(duì)調(diào)用該注解的方法做類似下面的事情。

這里假設(shè)你在方法f上定義了@Suspendable,同時(shí)去調(diào)用了有同樣注解的方法g,那么所有調(diào)用f的方法會(huì)插入一些字節(jié)碼,這些字節(jié)碼的邏輯就是記錄當(dāng)前Fiber棧上的狀態(tài),以便在未來可以動(dòng)態(tài)的恢復(fù)。(Fiber類似線程也有自己的棧)。在suspendable方法鏈內(nèi)Fiber的父類會(huì)調(diào)用Fiber.park,這樣會(huì)拋出SuspendExecution異常,從而來停止線程的運(yùn)行,好讓Quasar的調(diào)度器執(zhí)行調(diào)度。這里的SuspendExecution會(huì)被Fiber自己捕獲,業(yè)務(wù)層面上不應(yīng)該捕獲到。如果Fiber被喚醒了(調(diào)度器層面會(huì)去調(diào)用Fiber.unpark),那么f會(huì)在被中斷的地方重新被調(diào)用(這里Fiber會(huì)知道自己在哪里被中斷),同時(shí)會(huì)把g的調(diào)用結(jié)果(g會(huì)return結(jié)果)插入到f的恢復(fù)點(diǎn),這樣看上去就好像g的return是flocal variables了,從而避免了callback嵌套。

上面啰嗦了一大堆,其實(shí)簡(jiǎn)單點(diǎn)講就是,想辦法讓運(yùn)行中的線程棧停下來,好讓Quasar的調(diào)度器介入。JVM線程中斷的條件只有兩個(gè),一個(gè)是拋異常,另外一個(gè)就是return。這里Quasar就是通過拋異常的方式來達(dá)到的,所以你會(huì)看到我上面的代碼會(huì)拋出SuspendExecution。但是如果你真捕獲到這個(gè)異常,那就說明有問題了,所以一般會(huì)這么寫。

@Suspendable
public int f() {
  try {
    // do some stuff
    return g() * 2;
  } catch(SuspendExecution s) {
    //這里不應(yīng)該捕獲到異常.
    throw new AssertionError(s);
  }
}
與Golang性能對(duì)比

在github上無意中發(fā)現(xiàn)一個(gè)有趣的benchmark,大致是測(cè)試各種語言在生成百萬actor/Fiber的開銷skynet。
大致的邏輯是先生成10個(gè)Fiber,每個(gè)Fiber再生成10個(gè)Fiber,直到生成1百萬個(gè)Fiber,然后每個(gè)Fiber做加法累積計(jì)算,并把結(jié)果發(fā)到channel里,這樣一直遞歸到根Fiber。后將最終結(jié)果發(fā)到channel。如果邏輯沒有錯(cuò)的話結(jié)果應(yīng)該是499999500000。我們搞個(gè)Quasar版的,來測(cè)試一下性能。

所有的測(cè)試都是基于我的Macbook Pro Retina 2013later。Quasar-0.7.5:JDK8,JDK 1.8.0_91,Golang 1.6

public class Skynet {

  private static final int RUNS = 4;
  private static final int BUFFER = 1000; // = 0 unbufferd, > 0 buffered ; < 0 unlimited

  static void skynet(Channel c, long num, int size, int div) throws SuspendExecution, InterruptedException {
    if (size == 1) {
      c.send(num);
      return;
    }

    Channel rc = newChannel(BUFFER);
    long sum = 0L;
    for (int i = 0; i < div; i++) {
      long subNum = num + i * (size / div);
      new Fiber(() -> skynet(rc, subNum, size / div, div)).start();
    }
    for (int i = 0; i < div; i++)
      sum += rc.receive();
    c.send(sum);
  }

  public static void main(String[] args) throws Exception {
    //這里跑4次,是為了讓JVM預(yù)熱好做優(yōu)化,所以我們以最后一個(gè)結(jié)果為準(zhǔn)。
    for (int i = 0; i < RUNS; i++) {
      long start = System.nanoTime();

      Channel c = newChannel(BUFFER);
      new Fiber(() -> skynet(c, 0, 1_000_000, 10)).start();
      long result = c.receive();

      long elapsed = (System.nanoTime() - start) / 1_000_000;
      System.out.println((i + 1) + ": " + result + " (" + elapsed + " ms)");
    }
  }
}

golang的代碼我就不貼了,大家可以從github上拿到,我這里直接貼出結(jié)果。

platform time
Golang 261ms
Quasar 612ms

從Skynet測(cè)試中可以看出,Quasar的性能對(duì)比Golang還是有差距的,但是不應(yīng)該達(dá)到兩倍多吧,經(jīng)過向Quasar作者求證才得知這個(gè)測(cè)試并沒有測(cè)試出實(shí)際性能,只是測(cè)試調(diào)度開銷而已。

因?yàn)閟kynet方法內(nèi)部幾乎沒有做任何事情,只是簡(jiǎn)單的做了一個(gè)加法然后進(jìn)一步的遞歸生成新的Fiber而已,相當(dāng)于只是測(cè)試了Quasar生成并調(diào)度百萬Fiber所需要的時(shí)間而已。而Java里的加法操作開銷遠(yuǎn)比生成Fiber的開銷要低,因此感覺整體性能不如golang(golang的coroutine是語言級(jí)別的)。

實(shí)際上我們?cè)趯?shí)際項(xiàng)目中生成的Fiber中不可能只做一下簡(jiǎn)單的加法就退出,至少要花費(fèi)1ms做一些簡(jiǎn)單的事情吧,(Quasar里Fiber的調(diào)度差不多在us級(jí)別),所以我們考慮在skynet里加一些比較耗時(shí)的操作,比如隨機(jī)生成1000個(gè)整數(shù)并對(duì)其進(jìn)行排序,這樣Fiber里算是有了相應(yīng)的性能開銷,與調(diào)度的開銷相比,調(diào)度的開銷就可以忽略不計(jì)了。(大家可以把調(diào)度開銷想象成不定積分的常數(shù))。

下面我分別為兩種語言了加了數(shù)組排序邏輯,并插在響應(yīng)的Fiber里。

public class Skynet {

  private static Random random = new Random();
  private static final int NUMBER_COUNT = 1000;
  private static final int RUNS = 4;
  private static final int BUFFER = 1000; // = 0 unbufferd, > 0 buffered ; < 0 unlimited

  private static void numberSort() {
    int[] nums = new int[NUMBER_COUNT];
    for (int i = 0; i < NUMBER_COUNT; i++)
      nums[i] = random.nextInt(NUMBER_COUNT);
    Arrays.sort(nums);
  }

  static void skynet(Channel c, long num, int size, int div) throws SuspendExecution, InterruptedException {
    if (size == 1) {
      c.send(num);
      return;
    }
    //加入排序邏輯
    numberSort();
    Channel rc = newChannel(BUFFER);
    long sum = 0L;
    for (int i = 0; i < div; i++) {
      long subNum = num + i * (size / div);
      new Fiber(() -> skynet(rc, subNum, size / div, div)).start();
    }
    for (int i = 0; i < div; i++)
      sum += rc.receive();
    c.send(sum);
  }

  public static void main(String[] args) throws Exception {
    for (int i = 0; i < RUNS; i++) {
      long start = System.nanoTime();

      Channel c = newChannel(BUFFER);
      new Fiber(() -> skynet(c, 0, 1_000_000, 10)).start();
      long result = c.receive();

      long elapsed = (System.nanoTime() - start) / 1_000_000;
      System.out.println((i + 1) + ": " + result + " (" + elapsed + " ms)");
    }
  }
}
const (
    numberCount = 1000
    loopCount   = 1000000
)

//排序函數(shù)
func numberSort() {
    nums := make([]int, numberCount)
    for i := 0; i < numberCount; i++ {
        nums[i] = rand.Intn(numberCount)
    }
    sort.Ints(nums)
}

func skynet(c chan int, num int, size int, div int) {
    if size == 1 {
        c <- num
        return
    }
  //加了排序邏輯
    numberSort()
    rc := make(chan int)
    var sum int
    for i := 0; i < div; i++ {
        subNum := num + i*(size/div)
        go skynet(rc, subNum, size/div, div)
    }
    for i := 0; i < div; i++ {
        sum += <-rc
    }
    c <- sum
}

func main() {
    c := make(chan int)
    start := time.Now()
    go skynet(c, 0, loopCount, 10)
    result := <-c
    took := time.Since(start)
    fmt.Printf("Result: %d in %d ms.
", result, took.Nanoseconds()/1e6)
}
platform time
Golang 23615ms
Quasar 15448ms

最后再進(jìn)行一次測(cè)試,發(fā)現(xiàn)Java的性能優(yōu)勢(shì)體現(xiàn)出來了。幾乎是golang的1.5倍,這也許是JVM/JDK經(jīng)過多年優(yōu)化的優(yōu)勢(shì)。因?yàn)榧恿藰I(yè)務(wù)邏輯后,對(duì)比的就是各種庫(kù)以及編譯器對(duì)語言的優(yōu)化了,協(xié)程調(diào)度開銷幾乎可以忽略不計(jì)。

為什么協(xié)程在Java里一直那么小眾

其實(shí)早在JDK1的時(shí)代,Java的線程被稱為GreenThread,那個(gè)時(shí)候就已經(jīng)有了Fiber,但是當(dāng)時(shí)不能與操作系統(tǒng)實(shí)現(xiàn)N:M綁定,所以放棄了?,F(xiàn)在Quasar憑借ForkJoinPool這個(gè)成熟的線程調(diào)度庫(kù)。

另外,如果你希望你的代碼能夠跑在Fiber里面,需要一個(gè)很大的前提條件,那就是你所有的庫(kù),必須是異步無阻塞的,也就說必須類似于node.js上的庫(kù),所有的邏輯都是異步回調(diào),而自Java里基本上所有的庫(kù)都是同步阻塞的,很少見到異步無阻塞的。而且得益于J2EE,以及Java上的三大框架(SSH)洗腦,大部分Java程序員都已經(jīng)習(xí)慣了基于線程,線性的完成一個(gè)業(yè)務(wù)邏輯,很難讓他們接受一種將邏輯割裂的異步編程模型。

但是隨著異步無阻塞這股風(fēng)氣起來,以及相關(guān)的coroutine語言Golang大力推廣,人們?cè)絹碓街廊绾胃玫恼ジ蒀PU性能(讓CPU避免不必要的等待,減少上下文切換),阻塞的行為基本發(fā)生在I/O上,如果能有一個(gè)庫(kù)能把所有的I/O行為都包裝成異步阻塞的話,那么Quasar就會(huì)有用武之地,JVM上公認(rèn)的是異步網(wǎng)絡(luò)通信庫(kù)是Netty,通過Netty基本解決了網(wǎng)絡(luò)I/O問題,另外還有一個(gè)是文件I/O,而這個(gè)JDK7提供的NIO2就可以滿足,通過AsynchronousFileChannel即可。

剩下的就是如何將他們封裝成更友好的API了。目前能達(dá)到生產(chǎn)級(jí)別的這種異步工具庫(kù),JVM上只有Vert.x3,封裝了Netty4,封裝了AsynchronousFileChannel,而且Vert.x官方也出了一個(gè)相對(duì)應(yīng)的封裝了Quasar的庫(kù)vertx-sync。

Quasar目前是由一家商業(yè)公司Parallel Universe控制著,且有自己的一套體系,包括Quasar-actor,Quasar-galaxy等各個(gè)模塊,但是Quasar-core是開源的,此外Quasar自己也通過Fiber封裝了很多的第三方庫(kù),目前全都在comsat這個(gè)項(xiàng)目里。隨便找一個(gè)項(xiàng)目看看,你會(huì)發(fā)現(xiàn)其實(shí)通過Quasar的Fiber去封裝第三方的同步庫(kù)還是很簡(jiǎn)單的。

寫在最后

異步無阻塞的編碼方式其實(shí)有很多種實(shí)現(xiàn),比如node.js的提倡的Promise,對(duì)應(yīng)到Java8的就是CompletableFuture。

另外事件響應(yīng)式也算是一個(gè)比較流行的做法,比如ReactiveX系列,RxJava,Rxjs,RxSwift,等。我個(gè)人覺得RxJava是一個(gè)非常好的函數(shù)式響應(yīng)實(shí)現(xiàn)(JDK9會(huì)有對(duì)應(yīng)的JDK實(shí)現(xiàn)),但是我們不能要求所有的程序員一眼就提煉出業(yè)務(wù)里的functor,monad(這些能力需要長(zhǎng)期浸淫在函數(shù)式編程思想里),反而RxJava特別適合用在前端與用戶交互的部分,因?yàn)橛脩舻狞c(diǎn)擊滑動(dòng)行為是一個(gè)個(gè)真實(shí)的事件流,這也是為什么RxJava在Android端非?;鸬脑颍蠖嘶旧隙际峭ㄟ^Rest請(qǐng)求過來,每一個(gè)請(qǐng)求其實(shí)已經(jīng)限定了業(yè)務(wù)范圍,不會(huì)再有復(fù)雜的事件邏輯,所以基本上RxJava在Vert.x這端只是做了一堆的flatmap,再加上微服務(wù)化,所有的業(yè)務(wù)邏輯都已經(jīng)做了最小的邊界,所以順序的同步的編碼方式更適合寫業(yè)務(wù)邏輯的后端程序員。

所以這里Golang開了個(gè)好頭,但是Golang也有其自身的限制,比如不支持泛型,當(dāng)然這個(gè)仁者見仁智者見智了,包的依賴管理比較弱,此外Golang沒有線程池的概念,如果coroutine里的邏輯發(fā)生了阻塞,那么整個(gè)程序會(huì)hang死。而這點(diǎn)Vert.x提供了一個(gè)Worker Pool的概念,可以將需要耗時(shí)執(zhí)行的邏輯包到線程池里面,執(zhí)行完后異步返回給EventLoop線程。

下一篇我們來研究一下vertx-sync,讓vert.x里所有的異步編碼方式同步化,徹底解決Vert.x里的Callback Hell。

本文系力譜宿云LeapCloud旗下 MaxLeap 團(tuán)隊(duì)成員:劉小溪【原創(chuàng)】,轉(zhuǎn)載請(qǐng)務(wù)必注明作者及原創(chuàng)地址
原創(chuàng)首發(fā)地址:https://blog.maxleap.cn/archi...

第二篇續(xù)作:次時(shí)代Java編程(一):續(xù) vertx-sync實(shí)踐

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/65986.html

相關(guān)文章

  • Java多線程學(xué)習(xí)(七)并發(fā)編程些問題

    摘要:因?yàn)槎嗑€程競(jìng)爭(zhēng)鎖時(shí)會(huì)引起上下文切換。減少線程的使用。舉個(gè)例子如果說服務(wù)器的帶寬只有,某個(gè)資源的下載速度是,系統(tǒng)啟動(dòng)個(gè)線程下載該資源并不會(huì)導(dǎo)致下載速度編程,所以在并發(fā)編程時(shí),需要考慮這些資源的限制。 最近私下做一項(xiàng)目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項(xiàng)目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Jav...

    yimo 評(píng)論0 收藏0
  • Java多線程學(xué)習(xí)(七)并發(fā)編程些問題

    摘要:相比與其他操作系統(tǒng)包括其他類系統(tǒng)有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。因?yàn)槎嗑€程競(jìng)爭(zhēng)鎖時(shí)會(huì)引起上下文切換。減少線程的使用。很多編程語言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時(shí)至關(guān)重要。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)syn...

    dingding199389 評(píng)論0 收藏0
  • 時(shí)代Java編程():續(xù) vertx-sync實(shí)踐

    摘要:定時(shí)器例子之前通過調(diào)用定時(shí)器,需要傳一個(gè)回調(diào),然后所有的代碼邏輯都包在里面。這里定時(shí)器會(huì)阻塞在這一行,直到一秒后才會(huì)執(zhí)行下面的一行。 之前介紹過quasar,如果你希望在vert.x項(xiàng)目里使用coroutine的話,建議使用vertx-sync。本篇將介紹vertx-sync。 showImg(/img/bVzIsu); 本來打算另起一篇,寫其他方面的東西,但是最近比較忙,就先寫一篇實(shí)...

    vpants 評(píng)論0 收藏0
  • 協(xié)程原理】 - 為什么greenlet的狀態(tài)無法被保存

    摘要:特別是最火的協(xié)程框架也無法保存狀態(tài),讓人非常惋惜。但是因?yàn)闂5谋旧頍o法持久化,所以也就無法持久化。其難度在于,假設(shè)整個(gè)要持久化的調(diào)用棧全部都是內(nèi)的,比如純的。采取的是暴力地把整個(gè)棧區(qū)域拷貝到上的方式來保存其狀態(tài)。 python主流的協(xié)程實(shí)現(xiàn)有五種: cPython的generator cPython的greenlet cPython的fibers stackless python ...

    verano 評(píng)論0 收藏0
  • 如何用java 5分鐘實(shí)現(xiàn)個(gè)最簡(jiǎn)單的mysql代理服務(wù)器?

    摘要:用基于快速實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的代理服務(wù)器,只需要分鐘時(shí)間。監(jiān)控統(tǒng)計(jì)客戶端的請(qǐng)求情況,請(qǐng)求分布統(tǒng)計(jì)請(qǐng)求類型等,以此來優(yōu)化數(shù)據(jù)庫(kù)的使用。 用java8基于vert.x3 快速實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的mysql代理服務(wù)器,只需要5分鐘時(shí)間。 showImg(/img/bVz0vh); 什么是mysql 代理? mysql代理是介于client端和mysql服務(wù)端中間層服務(wù),如下圖所示: showImg(...

    JerryZou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<