1. 程式人生 > >Java的HTTP服務端響應式程式設計

Java的HTTP服務端響應式程式設計

傳統的Servlet模型走到了盡頭

傳統的Java伺服器程式設計遵循的是J2EE的Servlet規範,是一種基於執行緒的模型:每一次http請求都由一個執行緒來處理。

執行緒模型的缺陷在於,每一條執行緒都要自行處理套接字的讀寫操作。對於大部分請求來講,本地處理請求的速度很快,請求的讀取和返回是最耗時間的。也就是說大量的執行緒浪費在了遠端連線上,而沒有發揮出計算能力。但是需要注意一點,執行緒的建立是有開銷的,每一條執行緒都需要獨立的記憶體資源。JVM裡的-Xss引數就是用來調整執行緒堆疊大小的。而JVM堆的總大小侷限在了-Xmx引數上,因此一個正在執行的JVM伺服器能夠同時執行的執行緒數是固定的。

即便通過調整JVM引數,使其能夠執行更多執行緒。但是JVM的執行緒會對映成為作業系統的使用者執行緒,而作業系統依然只能排程有限數量的執行緒。例如,Linux系統可以參考這裡的討論:

Maximum number of threads per process in Linux?

此外,大量執行緒在切換的時候,也會產生上下文載入解除安裝的開銷,同樣會降低系統的效能。

可伸縮 IO

Doug Lea大神有一篇很經典的PPTScalable IO in Java,講述了一個更為優秀的伺服器模型。

一個可伸縮的網路服務系統應當滿足以下條件:

  1. 能夠隨著計算資源(CPU、記憶體、磁碟容量、網路頻寬等)的增加提高負載能力。
  2. 當網路負載增加超過能力的時候,能夠優雅降級,避免直接崩潰。例如,拒絕為超過能力範圍的請求提供服務,但對於能力範圍內的請求,依然提供服務。當流量洪峰過去之後,依然能夠正常執行。
  3. 當然高可用、高效能依然是必須的:例如低響應延遲、隨負載變化請求或釋放計算資源等。

作者給出的解決方案就是Reactor模式。

Reactor模式將耗時的IO資源封裝為handle物件。handle物件註冊在作業系統的核心裡,當物件滿足一定的條件時(可讀或者可寫),才會處理handle物件。在Reactor模式中,同步多路複用器負責處理handle物件的狀態變更,當滿足條件時,會呼叫handle物件註冊時提供的回撥函式。

同步多路複用器在一個單獨的執行緒裡專門處理IO連結。當請求讀取完畢之後,任務提交至工作執行緒池完成請求的解碼、處理、編碼等工作,最後將由多路複用器負責將結果返回給客戶端,而池內執行緒繼續處理下一個任務。相比JDK1.5之前的對每一次請求新建一個執行緒的方式,執行緒池能夠實現執行緒複用,降低建立回收執行緒的開銷,在應對密集計算負載的時候有更好的表現。同時,在多個執行緒上分別部署一個同步多路複用器,也可以更好地利用多核CPU的處理能力。

這樣,執行緒的任務分工就很明確,分別專門處理IO密集任務和專門處理CPU密集任務。

NIO普及艱難

從最早的select到後來Linux的epoll和BSD的Kqueue,作業系統的多路複用效能一直在不斷增強。

JDK 1.4引入了NIO模組,遮蔽了作業系統層面的細節,將各個系統的多路複用API做了統一封裝。JDK的NIO有以下幾個核心元件:

  • Buffer,一種容量在建立時被固定的資料容器
  • Charsets,負責資料的編解碼工作
  • Channels,對遠端連線的抽象
  • Selector,多路複用選擇器

網路連線封裝在Channels物件裡面。Channels在Selector上註冊感興趣的SelectionKey事件:可讀OP_READ、可寫OP_WRITE、可連線OP_CONNECT還有伺服器端套接字才有的可接入OP_ACCEPT。多路複用選擇器呼叫阻塞式select方法的時,在等待某一事件可用,然後就通知Channels執行相應的handler。Buffer是Channels實現讀寫操作的緩衝區。Charset用於對Buffer的內容進行編解碼。在NIO模式下,Selector能夠管理多個套接字的網路讀寫,避免了過多計算執行緒阻塞在讀寫請求上。

在JVM之外的世界裡,多路複用通過Nginx、基於V8引擎的Node.js早就大放異彩。但是Java NIO在生產環境裡的發展卻很慢。例如,Tomcat直到2016年釋出8.5版本的時候,才徹底移除BIO聯結器,完全擁抱NIO。

JDK NIO主要有這樣幾個問題比較麻煩:

  1. 首先是NIO為了提高資料收發效能,可以建立DirectBuffer物件。該物件的記憶體開闢在JVM堆之外,無法通過正常的GC收集器來回收,只能在JVM的老年代觸發全量GC的時候回收。而全量GC往往導致系統卡頓,降低響應效率。如果被動等待老年代區域自行觸發全量GC,又有可能造成堆外記憶體溢位。兩者之間的矛盾需要在開發的時候小心的平衡。
  2. 其次就是,JDK1.8依然存在的epoll bug:若Selector的輪詢結果為空,也沒有wakeup或新訊息處理,則發生空輪詢,CPU使用率100%。

Netty才是NIO該有的水準

作為一個第三方框架,Netty做到了JDK本應做到的事情。

Netty的資料容器ByteBuf更為優秀

ByteBuf同時維護兩個索引:讀索引和寫索引。從而保證容器物件能夠同時適配讀寫同時進行的場景。而NIO的Buffer卻需要執行一次flip操作來適應讀寫場景的切換。同時ByteBuf容器使用引用計數來手工管理,可以在引用計數歸零時通過反射呼叫jdk.internal.ref.Cleaner來回收記憶體,避免洩露。在GC低效的時候,選擇使用手工方式來管理記憶體,完全沒問題。

Netty的API封裝度更高

觀察一下Netty官網Tutorial給出的demo,只要幾十行程式碼就完成了一個具備Reactor模式的伺服器。ServerBootstrap的group方法定義了主套接字和子套接字的處理方式,例中使用的NioEventLoopGroup類為Java NIO + 多執行緒的實現方式。對於NIO的epoll bug,NioEventLoopGroup的解決方案是rebuildSelectors物件方法。這個方法允許在selector失效時重建新的selector,將舊的釋放掉。此外,Netty還通過JNI實現了自己的EpollEventLoopGroup,規避了NIO版本的bug。

Netty使用責任鏈模式實現了對server進出站訊息的處理,使得server的程式碼能夠更好的擴充套件和維護。

Netty在生產領域得到大量應用,Hadoop Avro、Dubbo、RocketMQ、Undertow等廣泛應用於生產領域的產品的通訊元件都選擇了Netty作為基礎,並經受住了考驗。

Netty是一個優秀的非同步通訊框架,但是主要應用在基礎元件中。因為Netty向開發者暴露出大量的細節,對於業務系統的開發仍然形成了困擾,所以沒法得到進一步的普及。

舉個例子。Netty使用ChannelFuture來接收傳入的請求。相比於JDK的Future實現,ChannelFuture可以新增一組GenericFutureListener來管理物件狀態,避免了反覆對Future物件狀態的詢問或阻塞獲取。這是個進步。但是,這些Listener都帶來了另一個問題——Callback hell。而巢狀的回撥程式碼往往難以維護。

對於Callback hell,我們可以做什麼

Netty做一個優秀的基礎元件就很好了。業務層面的問題就讓我們用業務層面的API來解決。

Java API的適應性不佳

JDK7以前的非同步程式碼難以組織

在JDK7以及之前,Java多執行緒的程式設計工具主要就是Thread、ExecutorService、Future以及相關的同步工具,實現出來的程式碼較為繁瑣、且效能不高。

Thread

舉個例子A,考慮一個場景有X、P、Q三個邏輯需要執行,其中X的執行需要在P、Q一起完成之後才啟動執行。

如果使用Thread,那麼程式碼會是這個樣子:

/* 建立執行緒 */
Thread a = new Thread(new Runnable() {
    @Override
    publicvoidrun() {
        /* P邏輯 */
    }
});

Thread b = new Thread(new Runnable() {
    @Override
    publicvoidrun() {
        /* Q邏輯 */
    }
});

/* 啟動執行緒 */
a.start();
b.start();

/* 等候a、b執行緒執行結束 */
try {
    a.join();
    b.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

/* 啟動X邏輯的執行 */
Thread c = new Thread(new Runnable() {
    @Override
    publicvoidrun() {
        /* X邏輯 */
    }
});
c.start();

...

上面這個程式碼,先不論執行緒建立的開銷,單從形式上看,執行緒內部的執行邏輯、執行緒本身的排程邏輯,還有必須捕獲的InterruptedException的異常處理邏輯混雜在一起,整體很混亂。假想一下,當業務邏輯填充在其中的時候,程式碼更難維護。

ThreadPoolExecutor、Future

ThreadPoolExecutor和Future有助於實現執行緒複用,但對於程式碼邏輯的規範沒什麼幫助。

ExecutorService pool = Executors.newCachedThreadPool();
Future<?> a = pool.submit(new Runnable() {
    @Override
    publicvoidrun() {
        /* P邏輯 */
    }
});
Future<?> b = pool.submit(new Runnable() {
    @Override
    publicvoidrun() {
        /* Q邏輯 */
    }
});

/* 獲取執行緒執行結果 * 依然要捕獲異常,處理邏輯 */
try {
    a.get();
    b.get();
    Future<?> c = pool.submit(new Runnable() {
        @Override
        publicvoidrun() {
            /* X邏輯 */
        }
    });
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

JDK8程式碼可讀性有了顯著提高

JDK8借鑑了相當多的函數語言程式設計的特點,提供了幾樣很稱手的工具。

CompleteableFuture和ForkJoinPool

如果要用CompleteableFuture實現上一個例子,可以這樣寫。

CompletableFuture<?> a = CompletableFuture.runAsync(() -> {
    /* P邏輯 */
}).exceptionally(ex -> {
    /* 異常處理邏輯 */
    return ...;
});
CompletableFuture<?> b = CompletableFuture.runAsync(() -> {
    /* Q邏輯 */
});
CompletableFuture<?> c = CompletableFuture.allOf(a, b).thenRun(() -> {
    /* X邏輯 */
});

有了lambda表示式的加持,例中的程式碼整體以執行緒內部邏輯為主,排程邏輯通過allOf()、thenRun()等方法名直觀地展示出來。特別是可選的異常捕獲邏輯,更是使得程式碼可讀性得到了極大的提高。

需要注意的是,CompletableFuture是可以使用指定ExecutorService來執行的。如果像上例那樣沒有指定ExecutorService物件,那麼會預設使用ForkJoinPool裡的靜態物件commonPool來執行。而ForkJoinPool.commonPool作為一個JVM例項中唯一的物件,也是Stream併發流的執行器,因此應當儘量保證CompletableFuture裡的邏輯不會阻塞執行緒。如果無法規避,可以使用ManagedBlocker來降低影響。

ForkJoinPool是JDK1.7提供的併發執行緒池,可以很好地應對計算密集型併發任務,特別適用於可以“分-治”的任務。傳統的ThreadPoolExecutor需要指定執行緒池裡的執行緒數量,而ForkJoinPool使用了一個相似但更有彈性的概念——“併發度”。併發度指的是池內的活躍執行緒數。對於可能的阻塞任務,ForkJoinPool設計了一個ManagedBlocker介面。當池內執行緒執行到ForkJoinPool.managedBlock(ForkJoinPool.ManagedBlocker blocker)方法時,執行緒池會新建一個執行緒去執行佇列裡的其他任務,並輪詢該物件的isReleasable方法,決定是否恢復執行緒繼續執行。JDK1.7裡的Phaser類原始碼用到了這個方法。

Stream

Stream流也是JDK8引入的一個很好的程式設計工具。

Stream物件通常通過Iterator、Collection來構造。也可以用StreamSupport的stream靜態方法來建立自定義行為的例項。

Stream流物件採用鏈式程式設計風格,可以制定一系列對流的定製行為,例如過濾、排序、轉化、迭代,最後產生結果。看個例子。

List<Integer> intList = List.of(1, 2, 3);

List<String> strList = intList.stream()
        .filter(k -> k>1)
        .map(String::valueOf)
        .collect(Collectors.toList());

上面這段程式碼中,intList通過stream方法獲取到流物件,然後篩選出大於1的元素,並通過String的valueOf靜態方法生成String物件,最後將各個String物件收集為一個列表strList。就像CompletableFuture的方法名一樣,Stream的方法名都是自描述的,使得程式碼可讀性極佳。

除此之外,Stream流的計算還是惰性的。Stream流物件的方法大致分為兩種:

  • 中間方法,例如filter、map等對流的改變
  • 終結方法,例如collect、forEach等可以結束流

只有在執行終結方法的時候,流的計算才會真正執行。之前的中間方法,都作為步驟記錄下來,但沒有實時地執行修改操作。

如果將例子裡的stream方法修改為parallelStream,那麼得到的流物件就是一個併發流,而且總在ForkJoinPool.commonPool中執行。

關於Stream,極力推薦Brian Goetz大神的系列文章Java Streams

還有一點問題

ForkJoinPool是一款強大的執行緒池元件,只要使用的得當,執行緒池總會保持一個合理的併發度,充分利用計算資源。

但是,CompleteableFuture也好,Stream也好,他們都存在一個相同的問題:無法通過後端執行緒池的負載變化,來調整前端的呼叫壓力。打比方說,當後端的ForkJoinPool.commonPool在全力運算而且佇列裡有大量的任務排隊時,新提交的任務很可能會有很高的響應延遲,但是前端的CompleteableFuture或者Stream沒有途徑去獲取這樣一個狀態,來延緩任務的提交。這種情況就違背了“響應式系統”的“靈敏性”要求。

來自第三方API的福音

Reactive Streams

Reactive Streams是一套標準,定義了一個運行於JVM平臺上的響應式程式設計框架實現所應該具備的行
為。

Reactive Streams規範衍生自“觀察者模式”,將前後依賴的邏輯流,拆解為事件和訂閱者。只有當事件發生變更時,感興趣的觀察者才隨之執行隨後的邏輯。Reactive Stream和JDK的Stream的理念有點接近,兩者都是注重對資料流的控制。緊耦合的邏輯流拆分為“訂閱-釋出”方式其實是一大進步。程式碼變得維護性更強,而且很容易隨著業務的需要按照訊息驅動模式拆解。

Reactive Streams規範定義了四種介面:

  • Publisher,負責生產資料流,每一個訂閱者都會呼叫subscribe方法來訂閱訊息。
  • Subscriber,就是訂閱者。
  • Subscription,其實就是一個訂單選項,相當於飯館裡的選單,由釋出者傳遞給訂閱者。
  • Processor,處於資料流的中間位置,即是訂閱者,也是新資料流的生產者。

當Subscriber呼叫Publisher.subscribe方法訂閱訊息時,Publisher就會呼叫Subscriber的onSubscribe方法,回傳一個Subscription選單。

Subscription選單包含兩個選擇:

  1. 一個是request方法,對資料流的請求,引數為所請求的資料流的數量,最大為Long.MAX_VALUE;
  2. 另一個是cancel方法,對資料流訂閱的取消,需要注意的是資料流或許會繼續傳送一段時間,以滿足之前的請求呼叫。

一個Subscription物件只能由同一個Subscriber呼叫,所以不存在物件共享的問題。因此即便Subscription物件有狀態,也不會危及邏輯鏈路的執行緒安全。

訂閱者Subscriber還需要定義三種行為:

  1. onNext,接受到資料流之後的執行邏輯;
  2. onError,當釋出出現錯誤的時候如何應對;
  3. onComplete,當訂閱的資料流傳送完畢之後的行為。

相比於Future、Thread那樣將業務邏輯和異常處理邏輯混雜在一起,Subscriber將其分別定義在三個方法裡,程式碼顯得更為清晰。java.util.Observer(在JDK9中開始廢棄)只定義了update方法,相當於這裡的onNext方法,相比之下Subscriber增加了對流整體的管理和對異常的處理。異常如果隨著呼叫鏈傳遞出去,除錯定位會非常麻煩。因此要重視onError方法,儘可能在訂閱者內部就處理這個異常。

儘管Reactive Streams規範和Stream都關注資料流,但兩者有一個顯著的區別。那就是Stream是基於生產一方的,生產者有多大能力,Stream就製造多少資料流。而Reactive Streams規範是基於消費者的。邏輯鏈下游可以通過對request方法引數的變更,通知上游調整生產資料流的速度。從而實現了“響應式系統”的“靈敏性”要求。這在響應式程式設計中,用術語“背壓”來描述。

Reactive Streams規範僅僅是一個標準,其實現又依賴其他組織的成果。其意義在於各家實現能夠通過這樣一個統一的介面,相互呼叫,有助於響應式框架生態的良性發展。Reactive Streams規範雖然是Netflix、Pivatol、RedHat等第三方大廠合作推出的,但已經隨著JDK9的釋出收編為官方API,位於java.util.concurrent.Flow之內。JDK8也可以在專案中直接整合相應的模組呼叫。

順便吐槽一下,JDK9官方文件給出的demo裡的資料流居然從Subscription裡生產出來,嚇得我反覆確認了一下Reactive Streams官方規範。

RxJava2

RxJava由Netfilx維護,實現了ReactiveX API規範。該規範有很多語言實現,生態很豐富。

Rx正規化最先是微軟在.NET平臺上實現的。2014年11月,Netfilx將Rx移植到JVM平臺,釋出了1.0穩定版本。而Reactive Streams規範是在2015年首次釋出,2017年才形成穩定版本。所以RxJava 1.x和Reactive Streams規範有很大出入。1.x版本迭代至2018年3月的1.3.8版本時,宣佈停止維護。

Netflix在2016年11月釋出2.0.1穩定版本,實現了和Reactive Streams規範的相容。2.x如今是官方的推薦版本。

RxJava框架裡主要有這些概念:

  • Observable與Observer。RxJava直接複用了“觀察者模式”裡的概念,有助於更快地被開發社群接受。Observeble和Publisher有一點差異:前者有“冷熱”的區分,“冷”表示只有訂閱的時候才釋出訊息流,“熱”表示訊息流的釋出與時候有物件訂閱無關。Publisher更像是“冷”的Observeble。
  • Operators,也就是操作符。RxJava和JDK Stream類似,但設計了更多的自描述的函式方法,並同樣實現了鏈式程式設計。這些方法包括但不限於轉換、過濾、結合等等。
  • Single,是一種特殊的Observable。一般的Observable能夠產生資料流,而Single只能產生一個數據。所以Single不需要onNext、onComplete方法,而是用一個onSuccess取而代之。
  • Subject,注意這個不是事件,而是介於Observable與Observer之間的中介物件,類似於Reactive Streams規範裡的Processor。
  • Scheduler,是一類執行緒池,用於處理併發任務。RxJava預設執行在主執行緒上,可以通過observeOn/subscribeOn方法來非同步呼叫阻塞式任務。

RxJava 2.x在Zuul 2、Hystrix、Jersey等專案都有使用,在生產領域已經得到了應用。

Reactor3

Reactor3有Pivotal來開發維護,也就是Spring的同門師弟。

整體上,Reactor3框架裡的概念和RxJava都是類似的。Mono和Flux都等同於RxJava的Single和Observable。Reactor3也使用自描述的操作符函式實現鏈式程式設計。

RxJava 2.x支援JVM 6+平臺,對老舊專案很友好;而Reactor3要求必須是JVM8+。所以說,如果是新專案,使用Reactor3更好,因為它使用了很多新的API,支撐很多函式式介面,程式碼可讀性維護性都更好。

背靠Spring大樹,Reactor3的設計目標是伺服器端的Java專案。Reactor社群針對伺服器端,不斷推出新產品,例如Reactor Netty、Reactor Kafka等等。但如果是Android專案,RxJava2更為合適(來自Reactor3官方文件的建議)。

老實講,Reactor3的文件內容更豐富。

什麼是響應式系統

響應式宣言裡面說的很清楚,一個響應式系統應當是:

  • 靈敏的:能夠及時響應
  • 有回覆性的:即使遇到故障,也能夠自行恢復、併產生回覆
  • 可伸縮的:能夠隨著工作負載的變化,自行呼叫或釋放計算資源;也能夠隨著計算資源的變化,相應的調整工作負載能力
  • 訊息驅動的:顯式的訊息傳遞能夠實現系統各元件解耦,各類元件自行管理資源排程。

構建響應式Web系統

Vert.X

Vert.X目前由Eclipse基金會維護,打造了一整套響應式Web系統開發環境,包括資料庫管理、訊息佇列、微服務、許可權認證、叢集管理器、Devops等等,生態很豐富。

Vert.X Core框架基於Netty開發,是一種事件驅動框架:每當事件可行時都會呼叫其對應的handler。在Vert.X裡,有專門的執行緒負責呼叫handler,被稱作eventloop。每一個Vert.X例項都維護了多個eventloop。

Vert.X Core框架有兩個重要的概念:Verticle和Event Bus。

Verticle

Verticle類似於Actor模型的Actor角色。

Actor是什麼?

這裡泛泛的說一下吧。

Actor模型主要針對於分散式計算系統。Actor是其中最基本的計算單元。每一個Actor都有一個私有的訊息佇列。Actor之間的通訊依靠傳送訊息。每一個Actor都可以併發地做到:

  1. 向其他Actor傳送訊息
  2. 建立新的Actor
  3. 指定當接收到下一個訊息時的行為

Verticle之間的訊息傳遞依賴於下面要說的Event Bus。

Vert.X為Verticle的部署提供了高可用特性:在Vert.X叢集中,如果一個節點的上執行的Veticle例項失效,其他節點就會重新部署一份新的Verticle例項。

Verticle只是Vert.X提供的一種方案,並非強制使用。

Event Bus

Event Bus是Vert.X框架的中樞系統,能夠實現系統中各元件的訊息傳遞和handler的註冊與登出。其訊息傳遞既支援“訂閱-釋出”模式,也支援“請求-響應”模式。

當多個Vert.X例項組成叢集的時候,各系統的Event Bus能夠組成一個統一的分散式Event Bus。各Event Bus節點相互之間通過TCP協議通訊,沒有依賴Cluster Manager。這是一種可以實現節點發現,提供了分散式基礎元件(鎖、計數器、map)等的元件。

Spring WebFlux

Spring5的亮點之一就是響應式框架Spring WebFlux,使用自家的Reactor3開發,但同樣支援RxJava。

Spring WebFlux的預設服務端容器是Reactor Netty,也可以使用Undertow或者Tomcat、Jetty的實現了Servlet 3.1 非阻塞API介面的版本。Spring WebFlux分別為這些容器實現了與Reactive Streams規範實現框架互動的介面卡(Adapter),沒有向用戶層暴露Servlet API。

Spring WebFlux的註解方式和Spring MVC很像。這有助於開發團隊快速適應新框架。而且Spring WebFlux相容Tomcat、Jetty,有助於專案運維工作的穩定性。

但如果是新的專案、新的團隊,給我大概會選Vert.X,因為Event Bus確實很吸引人。

參考資料

延伸閱讀

相關推薦

Java的HTTP服務響應程式設計

傳統的Servlet模型走到了盡頭傳統的Java伺服器程式設計遵循的是J2EE的Servlet規範,是一種基於執行緒的模型:每一次http請求都由一個執行緒來處理。執行緒模型的缺陷在於,每一條執行緒都要自行處理套接字的讀寫操作。對於大部分請求來講,本地處理請求的速度很快,請求

Java的HTTP服務響應編程

降級 java 並發 復用 obs 浪費 world 編碼 plantuml 快速 為什麽要響應式編程? 傳統的Servlet模型走到了盡頭 傳統的Java服務器編程遵循的是J2EE的Servlet規範,是一種基於線程的模型:每一次http請求都由一個線程來處理。 線程模

WebFlux響應程式設計基礎之 5 webflux服務開發講解

Spring5 非組塞的開發模式 SpringMvc 與 SpringWebFlux 對比 學習工作機制 工作思想 更加重要 Netty 很重要 讀一下 Netty原始碼 先垂直擴充套件 –》 後水平擴充套件 5-2 非同步serv

java-網路程式設計(TCP-服務響應客戶)

 網路程式設計詳解-TCP   一,TCP協議的特點               面向連線的協議(有傳送端就一定要有接收端)       

Vue-cli腳手架搭建移動響應專案及nodejs服務搭建

首先安裝腳手架及建立基於webpack專案 首先下載vue相關 cnpm install vue 安裝 vue-cli腳手架 全域性安裝 vue-cli $ cnpm install –global vue-cli 建立一個基於 webpack 模板的新

為何現在響應程式設計在業務開發微服務開發不普及

為何現在響應式程式設計在業務開發微服務開發不普及 **主要因為資料庫 IO,不是 NIO。** 不論是Java自帶的Future框架,還是 Spring WebFlux,還是 Vert.x,他們都是一種非阻塞的基於Ractor模型的框架(後兩個框架都是利用netty實現)。 在阻塞程式設計模式裡,任何一

移動使用rem同時適應安卓ios手機原理解析,移動響應開發

size screen bsp 應用 屏幕 來看 比例 忽略 基礎 rem單位大家可能已經很熟悉,rem是隨著html的字體大小來顯示代表寬度的方法,我們怎樣進行移動端響應式開發呢 瀏覽器默認的字體大小為16px 及1rem 等於 16px 如果我們想要使1rem等於 12

Servlet的服務響應

and 空行 auto name 使用 如果 調用 p s 包含 以下內容引用自http://wiki.jikexueyuan.com/project/servlet/server-response.html: 當一個Web服務器對瀏覽器響應一個HTTP請求時,響應通常包

移動響應布局+rem+calc()

nbsp bsp 改變 查詢 設計 程序 去百度 響應式布局 only 1.媒體查詢:@media only screen and (max-width: ) {},在最初做pc端時,使用各種媒體查詢,因為pc的屏幕分辨率總共就幾種,不嫌麻煩的重復使用類名。有很大的缺陷就是

web前端-移動響應與自適應

logs lac tro 可維護 禁止 網頁 藍色 媒體查詢 [0 一. 在HTML的頭部加入meta標簽     在HTML的頭部,也就是head標簽中增加meta標簽,告訴瀏覽器網頁寬度等於設備屏幕寬度,且不進行縮放,代碼如下: <meta name="view

pc響應-媒體查詢

styles 什麽 頁面 and -s lte div chrom lin 媒體查詢(@media):能在不同的條件下使用不同的樣式,使頁面在不同在終端設備下達到不同的渲染效果 列舉常用的pc屏幕寬度: 1024 1280 1366 1440 1680 1920

美團客戶響應框架EasyReact開源啦

ges 進行 抽象 iphone and mona func 可靠 next 前言 EasyReact 是一款基於響應式編程範式的客戶端開發框架,開發者可以使用此框架輕松地解決客戶端的異步問題。 目前 EasyReact 已在美團和大眾點評客戶端的部分業務中進行了實踐,並

封裝高可復用的服務響應SSC程序修復對象 --ServerResponse

msg orm ons implement php 服務端 lun mail ssa 在平時的SSC程序修復,需要請搜索dsluntan.com 編碼過程中,返回給前端的數據都會統一規範起來,用一個泛型來作為響應對象ServerResponse類@JsonSerialize

移動響應布局,rem動態更新

androi setattr att ont win ati fonts var fun (function(){ var fontSizeMatchDeviceWidth = function(){ var deviceWidth = document.do

移動響應

人的 w3c 防止 度量 渲染 n) 是否 錯位 cli 一、css3的@media媒體查詢 1、定義和使用 @media 可以針對不同的屏幕尺寸設置不同的樣式,特別是如果你需要開發響應式的頁面,@media 是非常有用的。當你重置瀏覽器大小的過程中,頁面也會

Android響應程式設計之RxJava2.0

前言 優點: 1、鏈式操作 2、非同步優化 實戰 先來個簡單的使用示例 Observable .create(new ObservableOnSubscribe<String>() {

angular2響應程式設計

響應式程式設計: 就是非同步資料流程式設計,例如一個單機的事件就是一個流。 是以觀察者模式為核心的,rxjs概念 什麼是觀察者模式? 有兩個物件,一個是可觀察物件(Observable流),一個是觀察者。 先在可觀察物件內部註冊一些觀察者物件,如果可觀察物件內部發生了變化的時候

springboot2.x簡單詳細教程--高階篇幅之響應程式設計(第十五章)

      一、SprinBoot2.x響應式程式設計簡介     簡介:講解什麼是reactive響應式程式設計和使用的好處     1、基礎理解:     

Rxjava2入門:函式響應程式設計及概述

Rxjava2入門教程一:https://www.jianshu.com/p/15b2f3d7141a Rxjava2入門教程二:https://www.jianshu.com/p/c8150187714c Rxjava2入門教程三:https://www.jianshu.com/p/6e7

響應程式設計在Android 中的一些探索

    響應式在前端領域已經變得十分流行,很多主流框架都採用響應式來進行頁面的展示重新整理。本文主要是探索一下響應式在移動端Android上的一些實踐,包括對響應式思想的理解,以及目前Android上實現響應式的一些手段,最後聊聊響應式在Android開發上的一些應用。