www涩-www黄网站-www黄色-www黄色com-国产免费拍拍视频在线观看网站-国产免费怕怕免费视频观看

響應(yīng)式API的設(shè)計(jì)、實(shí)現(xiàn)和應(yīng)用

2018-03-20 17:01:48 InfoQ  點(diǎn)擊量: 評(píng)論 (0)
這篇文章來自于SpringOne的一個(gè)演講。在過去的幾年里,Java世界中在大力推動(dòng)響應(yīng)式編程的。無論是NodeJS開發(fā)人員使用非阻塞api的成功,還是
這篇文章來自于SpringOne的一個(gè)演講。

在過去的幾年里,Java世界中在大力推動(dòng)響應(yīng)式編程的。無論是NodeJS開發(fā)人員使用非阻塞api的成功,還是引發(fā)延遲的微服務(wù)的爆炸式增長(zhǎng),還是僅僅是想要更有效地利用計(jì)算資源,許多開發(fā)人員都開始將響應(yīng)式編程看作一種可行的編程模型。 

幸運(yùn)的是,涉及到響應(yīng)式框架以及如何正確使用它們時(shí),Java開發(fā)人員被選擇給寵壞了。沒有太多編寫響應(yīng)式代碼的“錯(cuò)誤”方法,但是,這同時(shí)也是問題所在;也沒多少編寫響應(yīng)式代碼的“正確”方法。

在本文中,我們的目的是給你一些關(guān)于如何編寫響應(yīng)式代碼的意見。這些觀點(diǎn)來自多年來開發(fā)一個(gè)大規(guī)模的響應(yīng)式API的經(jīng)驗(yàn),雖然它們可能并不適合你,但我們希望它們?cè)谀汩_始你的響應(yīng)式之旅時(shí)能給你一些方向。

本文中的示例都來自于Cloud Foundry Java客戶端。這個(gè)項(xiàng)目使用Reactor項(xiàng)目的響應(yīng)式框架。我們?yōu)檫@個(gè)Java客戶端選擇Reactor的原因,是因?yàn)樗cSpring團(tuán)隊(duì)有緊密的集成,但是我們討論的所有概念也都適用于其他的響應(yīng)式框架,比如RxJava。如果你對(duì)Cloud Foundry有一些了解,這將很有幫助,但這不是必需的。這些例子有自解釋性命名,在解釋每個(gè)響應(yīng)式概念時(shí)它們將助你更好地理解。

響應(yīng)式編程是一個(gè)巨大的主題,它遠(yuǎn)遠(yuǎn)超出了本文的范圍,但是為了實(shí)現(xiàn)我們的目的,讓我們寬泛地把它定義為一種用更流暢的方式定義事件驅(qū)動(dòng)系統(tǒng)的方法,而不是傳統(tǒng)的命令式編程風(fēng)格。其目標(biāo)是將命令式邏輯轉(zhuǎn)換為異步、非阻塞、函數(shù)式的樣式,這種樣式更容易理解和推理。 

為這些做法(threads、NIO、callbacks等等)設(shè)計(jì)的命令式API并未考慮如何正確、可靠和方便地使用,許多情況下,在應(yīng)用程序代碼中使用這些API仍需要大量顯式地管理。響應(yīng)式框架的承諾是,這些關(guān)注點(diǎn)可以在幕后處理,從而讓開發(fā)人員能夠把主力精力放在應(yīng)用程序功能代碼的編寫上。

我應(yīng)該使用響應(yīng)式編程嗎?

在設(shè)計(jì)響應(yīng)式API時(shí),首先要問自己的問題是,你是否想要一個(gè)響應(yīng)式API! 響應(yīng)式api不可能適用于所有的一切。響應(yīng)式編程有顯而易見的缺點(diǎn)(目前最大的問題是調(diào)試,但框架和ide都正在積極解決此問題)。相反,當(dāng)價(jià)值明顯大于缺點(diǎn)時(shí),你就選擇響應(yīng)式API吧。在作出這個(gè)判斷時(shí),有幾個(gè)用于響應(yīng)式編程的模式非常適合。

網(wǎng)絡(luò)化

網(wǎng)絡(luò)請(qǐng)求本質(zhì)上就撇不開(相對(duì))較大的延遲,而且等待這些響應(yīng)返回通常是系統(tǒng)中最大的資源浪費(fèi)。在非響應(yīng)式應(yīng)用程序中,那些等待中的請(qǐng)求通常會(huì)阻塞線程并消耗堆棧內(nèi)存,空閑著等待響應(yīng)到達(dá)。遠(yuǎn)程故障和超時(shí)通常沒有得到系統(tǒng)地、明確地處理,因?yàn)樘峁┑腁PI不容易做到這一點(diǎn)。最后,遠(yuǎn)程調(diào)用的負(fù)載通常是未知的、無邊界的,導(dǎo)致堆內(nèi)存耗盡。響應(yīng)式編程與非阻塞IO相結(jié)合,解決了這類問題,因?yàn)樗鼮槟闾峁┝艘粋€(gè)清晰的和顯式的API。

高并發(fā)操作

它也很適合用于協(xié)調(diào)高并發(fā)操作(如網(wǎng)絡(luò)請(qǐng)求或可并行化cpu密集型計(jì)算)。響應(yīng)式框架,雖然允許顯式管理線程,但采用自動(dòng)線程管理也很出色。像.flatmap()這樣的操作符透明地并行化行為,最大化地利用可用資源。

大規(guī)模可擴(kuò)展應(yīng)用

每個(gè)鏈接一個(gè)線程的servlet 模型已經(jīng)為我們服務(wù)了很多年了。但是,隨著微服務(wù)的出現(xiàn),我們已經(jīng)開始看到應(yīng)用程序大規(guī)模地?cái)U(kuò)展(25、50甚至100個(gè)單個(gè)無狀態(tài)應(yīng)用程序的實(shí)例)來處理連接負(fù)載,即使CPU使用率處于空閑狀態(tài)。選擇非阻塞IO加響應(yīng)式編程效果更佳,打破了鏈接與線程間的這種聯(lián)系,使可用資源得到更有效的利用。很明顯,這樣的優(yōu)勢(shì)通常是驚人的。它常常需要在Tomcat上構(gòu)建一個(gè)應(yīng)用程序的更多實(shí)例,這些應(yīng)用程序需要成百上千的線程來處理相同的負(fù)載,就像同一應(yīng)用程序構(gòu)建在擁有8個(gè)線程的Netty上一樣。

雖然以上所列不能完全用來評(píng)判響應(yīng)式編程在哪里適用,但關(guān)鍵是要記住,如果你的應(yīng)用不適合以上任何一種,那么你用它可能只是徒增復(fù)雜度,而不會(huì)增加任何價(jià)值。

響應(yīng)式API應(yīng)該返回什么?

如果你回答了第一個(gè)問題,判定出你的應(yīng)用會(huì)從響應(yīng)式API得到收益,那么就到了設(shè)計(jì)API的時(shí)候了。決定你的響應(yīng)式API應(yīng)該返回什么基本類型是一個(gè)好的起點(diǎn)。

Java世界中的所有響應(yīng)式框架(包括Java 9的Flow)都是在響應(yīng)式流程規(guī)范之上通信的。這個(gè)規(guī)范定義了一個(gè)低級(jí)的交互API,但是它不被認(rèn)為是一個(gè)響應(yīng)式框架(也就是說,它未針對(duì)流指定可用的操作符)。

在Reactor 項(xiàng)目中有兩種主要的類型。Flux類型表示流經(jīng)該系統(tǒng)的0到N個(gè)值。Mono類型表示0到1個(gè)值。在Java客戶端中,我們幾乎只使用Mono,因?yàn)樗宄赜成涞絾蝹€(gè)請(qǐng)求、單個(gè)響應(yīng)模型。

Flux<Application> listApplications() {...}

Flux<String> listApplicationNames() {
  return listApplications()
    .map(Application::getName);
}

void printApplicationName() {
  listApplicationNames()
    .subscribe(System.out::println);
}

在本例中,listApplications()方法執(zhí)行一個(gè)網(wǎng)絡(luò)調(diào)用,并返回0到N個(gè)應(yīng)用程序?qū)嵗腇lux。然后,我們使用.map()操作符將每個(gè)應(yīng)用程序轉(zhuǎn)換為其名稱的字符串。然后將以應(yīng)用程序命名的Flux消費(fèi)并輸出到控制臺(tái)。

Flux<Application> listApplications() {...}

Mono<List<String>> listApplicationNames() {
  return listApplications()
    .map(Application::getName)
    .collectList();
}

Mono<Boolean> doesApplicationExist(String name) {
  return listApplicationNames()
    .map(names -> names.contains(name));
}

Mono并不像Flux那樣有一個(gè)流,但是因?yàn)樗鼈冊(cè)诟拍钌鲜且粋€(gè)元素的流,所以我們使用的操作符通常有相同的名稱。在這個(gè)例子中,除了映射到應(yīng)用程序名稱的Flux之外,我們還將這些名稱收集到一個(gè)List中。在這種情況下,包含該列表的Mono可以被轉(zhuǎn)換為一個(gè)boolean值,表示其中是否包含某個(gè)名稱。這可能與直覺不符,但是如果你正在處理的項(xiàng)目在邏輯上是一個(gè)項(xiàng)目的集合,而不是它們的流,那么返回一個(gè)集合的Mono也很正常(例如Mono>)。

與命令式API不同,void不是一個(gè)適當(dāng)?shù)捻憫?yīng)式返回類型。相反,每一個(gè)方法都必須返回一個(gè)Flux或者一個(gè)Mono。這可能看起來很奇怪(仍然有一些行為沒有任何返回呀!),但這是一個(gè)響應(yīng)流基本操作的結(jié)果。調(diào)用響應(yīng)式API的代碼執(zhí)行(例如.flatmap ().map()…)是構(gòu)建了一個(gè)數(shù)據(jù)到流的結(jié)構(gòu),但實(shí)際上并沒有轉(zhuǎn)換數(shù)據(jù)。只有在最后,當(dāng).subscribe()被調(diào)用時(shí),數(shù)據(jù)才會(huì)開始向流轉(zhuǎn)換,并在隨之完成轉(zhuǎn)換。這種惰性執(zhí)行正是為什么基于lambdas構(gòu)建響應(yīng)式編程的原因,以及為什么總要有返回類型,因?yàn)楸仨毜糜幸恍〇|西去.subscribe()。

void delete(String id) {
  this.restTemplate.delete(URI, id);
}

public void cleanup(String[] args) {
  delete("test-id");
}

上面這種的命令式阻塞示例可以返回void,因?yàn)樗木W(wǎng)絡(luò)調(diào)用會(huì)立即開始執(zhí)行,直到接收到響應(yīng)時(shí)才返回。

Mono<Void> delete(String id) {
  return this.httpClient.delete(URI, id);
}

public void cleanup(String[] args) {
  CountDownLatch latch = new CountDownLatch(1);

  delete("test-id")
    .subscribe(n -> {}, Throwable::printStackTrace, () -> latch::countDown);

  latch.await();
}

在這個(gè)響應(yīng)式示例中,網(wǎng)絡(luò)調(diào)用直到.subscribe()被調(diào)用后才開始,在delete()之后返回,因?yàn)樗怯脕砩烧{(diào)用的結(jié)構(gòu),而不是調(diào)用本身的結(jié)果。在本例中,我們使用返回0個(gè)條目的Mono,并在收到響應(yīng)后才發(fā)出onComplete()的信號(hào),這就相當(dāng)于void返回類型了。

方法的范圍

一旦你決定了你的API需要返回什么,你就需要考慮你的每個(gè)方法(API和實(shí)現(xiàn))將會(huì)做什么了。在該Java客戶端上,我們發(fā)現(xiàn)把方法設(shè)計(jì)小且可復(fù)用會(huì)帶來收益。它使每一種方法更容易組成更大的操作。這還能讓它們更靈活地組合成并行或順序操作。此外,它還使?jié)撛诘膹?fù)雜流程更具可讀性。

Mono<ListApplicationsResponse> getPage(int page) {
  return this.client.applicationsV2()
    .list(ListApplicationsRequest.builder()
      .page(page)
      .build());
}

void getResources() {
  getPage(1)
    .flatMapMany(response -> Flux.range(2, response.getTotalPages() - 1)
      .flatMap(page -> getPage(page))
      .startWith(response))
    .subscribe(System.out::println);
}

這個(gè)例子演示了我們?nèi)绾握{(diào)用一個(gè)分頁的API。第一個(gè)getPage()請(qǐng)求檢索結(jié)果的第一頁。在結(jié)果的第一頁中包括我們需要檢索的頁面總數(shù),以獲得完整的結(jié)果。因?yàn)間etPage()方法是小的、可重用的,而且沒有其他額外作用,所以我們可以重用該方法,并可以通過totalPages并行為第2頁進(jìn)行調(diào)用!

順序和并行協(xié)調(diào)

現(xiàn)在,幾乎所有顯著的性能改進(jìn)都來自對(duì)并發(fā)性的提升。我們知道這一點(diǎn),但許多系統(tǒng)的并發(fā)要么僅涉及傳入的連接,要么根本不并發(fā)。大部分這種情況都是源自這樣一個(gè)事實(shí),那就是實(shí)現(xiàn)一個(gè)高度并發(fā)的系統(tǒng)又困難又容易出錯(cuò)。響應(yīng)式編程的一個(gè)重要優(yōu)點(diǎn)是,你可以定義操作之間的順序和并行關(guān)系,并讓框架確定利用可用資源的最佳方式

大云網(wǎng)官方微信售電那點(diǎn)事兒

責(zé)任編輯:售電衡衡

免責(zé)聲明:本文僅代表作者個(gè)人觀點(diǎn),與本站無關(guān)。其原創(chuàng)性以及文中陳述文字和內(nèi)容未經(jīng)本站證實(shí),對(duì)本文以及其中全部或者部分內(nèi)容、文字的真實(shí)性、完整性、及時(shí)性本站不作任何保證或承諾,請(qǐng)讀者僅作參考,并請(qǐng)自行核實(shí)相關(guān)內(nèi)容。
我要收藏
個(gè)贊
?
主站蜘蛛池模板: 国产一级一国产一级毛片| 国内xxxx乱子另类| 久久精品国产亚洲| 女人张开双腿让男人| 精品久久久久久影院免费| 看片网站在线| 毛片国产| 日韩国产免费| 久久久久久久国产精品影院| 欧美精品一区二区三区免费播放| 久久视频在线| 国产一区二区久久久| 精品一区二区三区五区六区| 国内交换一区二区三区| 韩国自拍偷自拍亚洲精品| 成人在线视频国产| 亚洲一区二区三区久久精品| 91精品成人| 亚洲精品69| 色日韩| 久久亚洲精品视频| 九九黄色影院| 成人欧美网站免费| 亚洲人成影院午夜网站| 性色综合| 久久久久久九九| 和老外3p爽粗大免费视频| 国产成人在线播放| 亚洲精品亚洲人成毛片不卡| 特黄特色三级在线播放| 久久经典免费视频| 国产成人久久精品麻豆二区| 91国内精品视频| 日本69xxxxxxxxx69| 日韩欧美在线视频观看| 黄色毛片在线| 99视频在线观看免费| 亚洲一级毛片免观看| 久久久在线| 国产成人mv在线观看入口视频| 中文字幕一二三区|