摘要:更新補(bǔ)充實(shí)現(xiàn)細(xì)節(jié)怎么找到織入實(shí)現(xiàn)在塊中,如何處理什么情況下在前后可以不織入也能正常運(yùn)行簡介說到協(xié)程,很多人會想到等語言,其實(shí)上也有蠻多的實(shí)現(xiàn),如等,本文主要介紹其中一種實(shí)現(xiàn),相對來說流行度更好一些如果之前沒有接觸過協(xié)程用戶級輕量級線程,
20160827更新
補(bǔ)充method instrument實(shí)現(xiàn)細(xì)節(jié)(怎么找到suspend call、switch case織入實(shí)現(xiàn)、suspend call在try catch塊中,如何處理、什么情況下在suspend call前后可以不織入也能正常運(yùn)行)
說到協(xié)程(Coroutine),很多人會想到go,lua,erlang等語言,其實(shí)JVM上也有蠻多的實(shí)現(xiàn),如PicoThread,Kilim,Quasar等,本文主要介紹其中一種Coroutine實(shí)現(xiàn) -- Quasar Fiber,Quasar Fiber相對來說流行度更好一些,如果之前沒有接觸過協(xié)程(用戶級輕量級線程),可以看下What are fibers、Coroutine
那么為什么要使用協(xié)程?
協(xié)程可以用同步的編程方式達(dá)到或接近于純異步的性能,而沒有異步帶來的Callback hell,雖然有很多機(jī)制或模式解決或解耦callback hell的問題, 但同步的編程方式更容易維護(hù)和理解(風(fēng)格之爭是另外一個話題了,有興趣可以看下akka跟fiber的比較)
相比于os thread,fiber不管在內(nèi)存資源還是調(diào)度上都比前者輕量的多,相對于thread blocking, fiber blocking可以達(dá)到比前者大幾個數(shù)量級的并發(fā)度,更有效的利用CPU資源(運(yùn)行fiber的worker線程并沒有block)
具體大家可以看下Why and When use Fiber
好像是個神奇的東西呢,咋實(shí)現(xiàn)的
相比于callback接口回調(diào)的異步框架,Coroutine這個暫停和恢復(fù)在沒有JVM支持下,比較難以理解,是怎么做到的?有沒有什么魔法?其實(shí)JVM中Coroutine的實(shí)現(xiàn)方式有很多(implementing-coroutines-in-java),Quasar Fiber則是通過字節(jié)碼修改技術(shù)在編譯或載入時織入必要的上下文保存/恢復(fù)代碼,通過拋異常來暫停,恢復(fù)的時候根據(jù)保存的上下文(Continuation),恢復(fù)jvm的方法調(diào)用棧和局部變量,Quasar Fiber提供相應(yīng)的Java類庫來實(shí)現(xiàn),對應(yīng)用有一定的侵入性(很小)
Quasar Fiber 主要有 Instrument + Continuation + Scheduler幾個部分組成
Instrument 做一些代碼的植入,如park前后上下文的保存/恢復(fù)等
Continuation 保存方法調(diào)用的信息,如局部變量,引用等,用戶態(tài)的stack,這個也是跟akka等基于固定callback接口的異步框架最大的區(qū)別
Scheduler 調(diào)度器,負(fù)責(zé)將fiber分配到具體的os thread執(zhí)行
下面具體介紹下Quasar Fiber的實(shí)現(xiàn)細(xì)節(jié),最好先閱讀下quasar官方文檔,不是很長
Instrument Weavingquasar fiber的運(yùn)行需要織入一些指令,用于調(diào)用棧的保存和恢復(fù),quasar提供了三種方式進(jìn)行織入(AOT、javaagent、ClassLoader)
quasar 會對我們的代碼進(jìn)行static call-site分析,在必要的地方織入用于保存和恢復(fù)調(diào)用棧的代碼。
哪些方法需要call site分析?這里需要顯式的mark(jdk9不需要),如下
方法帶有Suspendable 注解
方法帶有SuspendExecution
方法為classpath下/META-INF/suspendables、/META-INF/suspendable-supers指定的類或接口,或子類
符合上面條件的method,quasar會對其做call site分析,也許為了效率,quasar并沒有對所有方法做call site分析
方法內(nèi)哪些指令需要instrument(在其前后織入相關(guān)指令)?
調(diào)用方法帶有Suspendable 注解
調(diào)用方法帶有SuspendExecution
調(diào)用方法為classpath下/META-INF/suspendables、/META-INF/suspendable-supers指定的類或接口,或子類
主要為了解決第三方庫無法添加Suspendable注解的問題
通過反射調(diào)用的方法
動態(tài)方法調(diào)用 MethodHandle.invoke
Java動態(tài)代理InvocationHandler.invoke
Java 8 lambdas調(diào)用
注意,instrument是在class loading的時候,不是runtime,所以這里call site分析的比如virtual invoke指令是編譯期決定的,這里比較容易犯錯,我總結(jié)了如下兩點(diǎn)
1.基于接口或基類編譯的代碼,如果實(shí)現(xiàn)類有可能suspend,那么需要在接口或基類中添加suspendable annotation或suspend異常
2.如果實(shí)現(xiàn)類會suspend,需要添加suspendable annotation或suspend異常,當(dāng)然可以把所有實(shí)現(xiàn)類都聲明成suspendable(如果方法里找不到suspend的調(diào)用,該方法將不被instrument,所以也沒有overhead,盡管這個overhead非常微?。?/p>
接下來我們簡單看下quasar instrument都織入了哪些代碼
從上圖可以看出,quasar instrument主要在park()前保存相關(guān)的局部變量和pc,再fiber恢復(fù)執(zhí)行的時候通過pc(switch case跳轉(zhuǎn)的程序計(jì)數(shù)器,非寄存器pc) jump到park()之后的代碼并恢復(fù)局部變量,另外在方法調(diào)用前后還會push/pop相關(guān)的Contiuation
instrument還會對JUC(java.util.concurrent)中的Thread.park,替換成Fiber.park,這樣park to thread就變成park to fiber,所以使用juc的代碼,可以不用修改的跑在Fiber上
quasar在織入代碼的同時,會對處理的類和方法加上Instrumented注解,以在運(yùn)行期檢查是否Instrumented,Instrumented注解包含了一個suspendableCallSites數(shù)組,用來存放方法體內(nèi)suspendable call的line number
contiuations/stack詳細(xì)請看contiuations章節(jié)
不管哪種織入方式,都是通過創(chuàng)建QuasarInstrumentor來處理Class的字節(jié)流
QuasarInstrumentor內(nèi)部使用ASM來處理Class的字節(jié)流,通過SuspendableClassifier類來判斷是否需要instrument
SuspendableClassifier有兩個子類,分別為DefaultSuspendableClassifier和SimpleSuspendableClassifier
DefaultSuspendableClassifier 掃描classpath下SuspendableClassifier的實(shí)現(xiàn),并且調(diào)用其接口判斷是否需要instrument,也會調(diào)用SimpleSuspendableClassifier
SimpleSuspendableClassifier 通過/META-INF/suspendables、/META-INF/suspendable-supers判斷
Method Instrument實(shí)現(xiàn)細(xì)節(jié)Quasar-core.jar包中suspendable-supers包含java nio及juc lock/future等接口,因?yàn)檫@些接口無法改變簽名,而quasar織入是在編譯或載入時,無法知道具體實(shí)現(xiàn)類是否Suspendable,所以需顯式指定
這里是整個Quasar Fiber是實(shí)現(xiàn)原理中最為關(guān)鍵的地方,也是大家疑問最多的地方,大家有興趣可以看下源代碼,大概1000多行的ASM操作,既可以鞏固JVM知識又能深入原理理解Fiber,這里我不打算引入過多ASM的知識,主要從實(shí)現(xiàn)邏輯上進(jìn)行介紹
InstrumentClass 繼承ASM的ClassVisitor,對Suspendable的方法前后進(jìn)行織入
InstrumentClass visitEnd中會創(chuàng)建InstrumentMethod,具體織入的指令在InstrumentMethod中處理
結(jié)合上面的instrument示例代碼圖,不妨先思考幾個問題
怎么找到suspend call
怎么保存、恢復(fù)局部變量,棧幀等
switch case跳轉(zhuǎn)如何織入
suspend call在try catch塊中,如何處理
什么情況下在suspend call前后可以不織入也能正常運(yùn)行
1.怎么找到suspend call
InstrumentMethod.callsSuspendables這個方法會遍歷方法的instructions,
如果instruction是method invoke,則判斷是否為suspend call(判斷邏輯見上面章節(jié))
如果instruction為suspend call,則把instrunction序號和source line number分別紀(jì)錄到suspCallsBcis及suspCallsSourceLines這兩個數(shù)組,供后面邏輯使用
2.switch case跳轉(zhuǎn)織入是如何實(shí)現(xiàn)的
現(xiàn)在我們知道了怎么找到method中的suspend call,那如何把這些suspend calls拆分成instrument示例圖中那樣呢(switch case,pc...)
這個拆分過程在InstrumentMethod.collectCodeBlocks
根據(jù)上面計(jì)算的suspend call的數(shù)組,分配label數(shù)組,然后根據(jù)pc計(jì)數(shù)器(詳細(xì)見后續(xù)章節(jié))進(jìn)行跳轉(zhuǎn)label
label是JVM里用于jump類指令,如(GOTO,IFEQ,TABLESWITCH等)
quasar會把織入的上下文保存恢復(fù)指令及代碼原始的指令生成到對應(yīng)label
3.怎么保存、恢復(fù)局部變量,棧幀
- 在方法開始執(zhí)行 1.調(diào)用Stack.nextMethodEntry,開啟新的method frame - 在方法結(jié)束執(zhí)行 1.Stack.popMethod, 進(jìn)行出棧 - 在調(diào)用Suspendable方法之前,增加以下邏輯 1.調(diào)用Stack.pushMethod 保存棧幀信息 2.依次調(diào)用Stack.put保存操作數(shù)棧數(shù)據(jù) 3.依次調(diào)用Stack.put保存局部變量 - 在Suspendable方法調(diào)用后 1.依次調(diào)用Stack.get恢復(fù)局部變量 2.依次調(diào)用Stack.get恢復(fù)操作數(shù)棧 恢復(fù)局部變量和操作數(shù)棧的區(qū)別是前者在get后調(diào)用istore
因?yàn)镾tack.put有3個參數(shù),所以這里每個put其實(shí)是多條jvm指令
aload_x //如果是保存操作數(shù)棧,這條指令不需要,因?yàn)橹狄呀?jīng)在操作數(shù)棧了 aload_x //load Stack引用 iconst_x //load Stack idx invokestatic co/paralleluniverse/fibers/Stack:push (Ljava/lang/Object;Lco/paralleluniverse/fibers/Stack;I)V
/** Stack.put會根據(jù)不同類型進(jìn)行處理,Object或Array保存到dataObject[],其他保存到dataLong[] **/ public static void push(long value, Stack s, int idx) public static void push(float value, Stack s, int idx) public static void push(double value, Stack s, int idx) public static void push(Object value, Stack s, int idx) public static void push(int value, Stack s, int idx)
java編譯期可知局部變量表和操作數(shù)棧個數(shù),上面put或get依賴這些信息,Stack具體邏輯見后面章節(jié)
4.什么情況下在suspend call前后可以不織入也能正常運(yùn)行
這里其實(shí)是一個優(yōu)化,就是如果method內(nèi)部只有一個suspend call,且前后沒有如下指令
side effects,包括方法調(diào)用,屬性設(shè)置
向前jump
monitor enter/exit
那么,quasar并不會對其instrument,也就不需要collectCodeBlocks分析,因?yàn)椴恍枰4?、恢?fù)局部變量
5.suspend call在try catch塊中,如何處理
如果suspend call在一個大的try catch中,而我們又需要在中間用switch case切分,似乎是個比較棘手的問題,
所以在織入代碼前,需要對包含suspend call的try catch做切分,將suspend call多帶帶包含在try catch當(dāng)中,通過ASM MethodNode.tryCatchBlocks.add添加新try catch塊,
quasar先獲取MethodNode的tryCatchBlocks進(jìn)行遍歷,如果suspend call的指令序號在try catch塊內(nèi),那么就需要切分,以便織入代碼
下面介紹下Quasar Fiber中的提供給用戶的類和接口
Strand是quasar里對Thread和Fiber統(tǒng)一的抽象,F(xiàn)iber是Strand的用戶級線程實(shí)現(xiàn),Thread是Strand內(nèi)核級線程的實(shí)現(xiàn)
Fiber主要有幾下幾個功能
new@SuppressWarnings("LeakingThisInConstructor") public Fiber(String name, FiberScheduler scheduler, int stackSize, SuspendableCallabletarget)
屬性 | 類型 | 說明 |
---|---|---|
name | String | fiber名稱 |
scheduler | FiberScheduler | 調(diào)度器,默認(rèn)為FiberForkJoinScheduler |
stackSize | int | stack大小,默認(rèn)32 |
target | SuspendableCallable |
具體業(yè)務(wù)代碼,在SuspendableCallable.run()里 |
構(gòu)造函數(shù)主要完成以下幾件事情
設(shè)置state為State.NEW
初始化Stack(用于保存fiber調(diào)用棧信息,Continuations的具體實(shí)現(xiàn))
校驗(yàn)target是否Instrumented
將當(dāng)前fiber封裝成一個可以由scheduler調(diào)度的task,默認(rèn)為FiberForkJoinTask
保存Thread的inheritableThreadLocals和contextClassLoader到Fiber
startFiber.start() 邏輯比較簡單,如下
將fiber state切換到State.STARTED
調(diào)用task的submit,提交給scheduler運(yùn)行
這里默認(rèn)的為FiberForkJoinScheduler,F(xiàn)iberForkJoinScheduler會提交到內(nèi)部的ForkJoinPool,并hash到其中一個work queue
fiber scheduler的worker thread從work quere獲取到task,并調(diào)用fiber.exec()
fiber.exec()主要步驟如下
cancel timeout task
將Thread的threadlocals、inheritableThreadLocals、contextClassLoader分別與fiber的互換,實(shí)現(xiàn)了local to fiber而不是local to thread,這里需要特別注意
所以基于thread local和context classloader的代碼基本上都能運(yùn)行在fiber上
state = State.RUNNING;
運(yùn)行業(yè)務(wù)邏輯(方法fiber.run())
state = State.TERMINATED;
Fiber暫停時如何處理
fiber task切換有兩種方式,一種是fiber task正常結(jié)束, 一種是fiber task拋SuspendExecution
fiber.exec()里會catch SuspendExecution,并交出執(zhí)行權(quán)限,具體步驟如下
stack sp = 0; // fiber恢復(fù)執(zhí)行需要從最開始的frame恢復(fù)
設(shè)置fiber狀態(tài) TIMED_WAITING/WAITING
恢復(fù)線程的Thread的threadlocals、inheritableThreadLocals、contextClassLoader
調(diào)用棧信息已經(jīng)在park()之前保存到stack中(見instrument章節(jié)),所以這里無需處理
park暫停當(dāng)前fiber的執(zhí)行,并交出執(zhí)行權(quán)
fiber task狀態(tài): RUNNABLE -> PARKING -> PARKED
fiber狀態(tài): RUNNING -> WAITING
Fiber.park方法如下,只能在當(dāng)前fiber調(diào)用
static boolean park(Object blocker, ParkAction postParkActions, long timeout, TimeUnit unit) throws SuspendExecution
park主要邏輯如下
設(shè)置fiber狀態(tài)
如果設(shè)置了timeout,則向FiberTimedScheduler新增ScheduledFutureTask,用于超時檢查
設(shè)置fiber.postPark = postParkActions,用于上面exec方法捕獲異常后執(zhí)行
拋異常,移交執(zhí)行權(quán)限, 后續(xù)邏輯見exec章節(jié)移交執(zhí)行權(quán)限
unpark恢復(fù)fiber的執(zhí)行
fiber task狀態(tài): PARKED -> RUNNABLE
fiber狀態(tài): WAITING -> RUNNING
unpark主要也是做兩件事情,一是設(shè)置狀態(tài),二是把fiber task重新submit到scheduler
狀態(tài)切換這里除了手工調(diào)用fiber的park,unpark來暫停和恢復(fù)fiber外,可以用FiberAsync類來將基于callback的異步調(diào)用封裝成fiber blocking,基于fiber的第三方庫comsat就是通過將bio替換成nio,然后再封裝成FiberAsync來實(shí)現(xiàn)的,FiberAsync可參考http://blog.paralleluniverse....
fiber運(yùn)行狀態(tài)由兩部分組成,一個是fiber本身的狀態(tài),一個是scheduler task的狀態(tài)
fiber狀態(tài)
狀態(tài) | 描述 |
---|---|
NEW | Strand created but not started |
STARTED | Strand started but not yet running |
RUNNING | Strand is running |
WAITING | Strand is blocked |
TIMED_WAITING | Strand is blocked with a timeout |
TERMINATED | Strand has terminated |
task狀態(tài),這里以默認(rèn)的FiberForkJoinTask為例
狀態(tài) | 描述 |
---|---|
RUNNABLE | 可運(yùn)行 |
LEASED | unpark時狀態(tài)是RUNNABLE,設(shè)置為LEASED |
PARKED | 停止 |
PARKING | 停止中 |
運(yùn)行狀態(tài)切換圖
ContinuationFiber/Coroutine = Continuation + scheduler可以看出,Continuation在Fiber中是至關(guān)重要的,他保存了fiber恢復(fù)執(zhí)行時的必要數(shù)據(jù),如pc,sp等
Quasar 中Continuation的實(shí)現(xiàn)為Stack類
StackStack類是quasar 對Fiber Continuation的實(shí)現(xiàn)類,該類由quasar instrument調(diào)用,以保存和恢復(fù)方法調(diào)用棧信息
屬性 | 類型 | 說明 |
---|---|---|
sp | int | 代表當(dāng)前操作的frame序號 |
dataLong | long[] | holds primitives on stack as well as each method"s entry |
dataObject | Object[] | holds refs on stack,防止jvm gc回收方法局部對象 |
dataLong中每一個long,代表一個method frame,具體定義如下
entry (PC) : 14 bits, 程序計(jì)數(shù)器,用于swich case跳轉(zhuǎn)
num slots : 16 bits, 當(dāng)前method frame占用多少個slot
prev method slots : 16 bits , 上一個method frame占用多少個slot,主要用于pop跳轉(zhuǎn)
我簡單畫了一個stack例子,其中pc,slots,prev slots用逗號分隔,xxxxxx代表method frame額外的一些數(shù)據(jù)
下面idx和data分別代碼dataLong的序號和內(nèi)容
idx | data | 說明 |
---|---|---|
5 | 0L | 下一個frame的存儲位置,sp指向該節(jié)點(diǎn) |
4 | xxxxxxx | 方法2局部變量c |
3 | xxxxxxx | 方法2局部變量b |
2 | 7 , 3 , 2 | 方法2,pc計(jì)數(shù)器為7,占用3個slot,上一個方法占用2個slots |
1 | xxxxxxx | 方法1局部變量a |
0 | 1,2 , 0 | 方法1,pc計(jì)數(shù)器為1,占用2個slot,上一個方法占用0個slots |
quasar會在instrument階段織入stack/continuation邏輯,具體如下
調(diào)用Suspendable方法之前,調(diào)用Stack.pushMethod
在Suspendable方法開始, 調(diào)用Stack.nextMethodEntry
在Suspendable方法結(jié)束, 調(diào)用Stack.popMethod
下面我們依次看下這幾個方法的邏輯
Stack.pushMethod
保存當(dāng)前pc
保存當(dāng)前slots數(shù)量
將下一個frame設(shè)置成0L
Stack.nextMethodEntry
將sp移動到當(dāng)前frame位置
將上一個freme的slots數(shù)量設(shè)置到當(dāng)前frame的prev slots字段
Stack.popMethod
按照當(dāng)前frame的prev slots進(jìn)行出棧操作
Schedulerscheduler顧名思義,是執(zhí)行fiber代碼的地方,quasar里用ForkJoinPool做為默認(rèn)scheduler的線程池,
ForkJoinPool的優(yōu)勢這里不再強(qiáng)調(diào),我們主要關(guān)注下Quasar中如何使用ForkJoinPool來調(diào)度fiber task
FiberForkJoinSchedulerquasar里默認(rèn)的fiber task scheduler,是JUC ForkJoinPool的wrapper類, ForkJoinPool具體細(xì)節(jié)參考ForkJoinPool
//主要屬性 private final ForkJoinPool fjPool;//具體執(zhí)行task的線程池 private final FiberTimedScheduler timer;//監(jiān)控fiber timeout的scheduler private final SetFiberForkJoinTaskactiveThreads;//保存fiber worker線程
wrapper了fiber的ForkJoinTask
//主要屬性 private final ForkJoinPool fjPool; private final FiberFiberTimedSchedulerfiber;
quasar自實(shí)現(xiàn)的timeout scheduler,用于fiber timeout的處理
FiberTimedScheduler默認(rèn)的work queue為SingleConsumerNonblockingProducerDelayQueue,這是一個多生產(chǎn)單消費(fèi)的無鎖隊(duì)列,內(nèi)部是一個lock-free的基于skip list的優(yōu)先級鏈表,有興趣可以看下具體的實(shí)現(xiàn),也值得一看
scheduler實(shí)現(xiàn)邏輯就比較簡單了,從SingleConsumerNonblockingProducerDelayQueue內(nèi)部的優(yōu)先級隊(duì)列取數(shù)據(jù),如果超時了則調(diào)用fiber.unpark()
可以通過JMX監(jiān)控fiber的運(yùn)行狀態(tài),work queue的堆積,fiber的數(shù)量,調(diào)度延遲等
comsatcomsat在quasar fiber基礎(chǔ)上提供了一些庫,使得跟fiber的集成更加容易,比如與servlet、springboot、drapwizard集成
https://github.com/puniverse/...
遇到的問題與解決COMSAT (or Comsat) is a set of open source libraries that integrate Quasar with various web or enterprise technologies (like HTTP services and database access). With Comsat, you can write web applications that are scalable and performing and, at the same time, are simple to code and maintain.
Comsat is not a web framework. In fact, it does not add new APIs at all (with one exception, Web Actors, mentioned later). It provides implementation to popular (and often, standard) APIs like Servlet, JAX-RS, and JDBC, that can be used efficiently within Quasar fibers.
本人在應(yīng)用中集成Fiber的時候遇到了不少問題,有些問題也反映了Quasar Fiber不是很完善,這里列出來供大家參考下
Netty PoolByteBufAllocator 在 Fiber調(diào)用 導(dǎo)致Memory Leak由于Quasar字節(jié)碼的處理,ThreadLocal在fiber上調(diào)用,實(shí)際是"local to Fiber",而不是"local to Thread", 如果要繞過Fiber取underlying的ThreadLocal,需要用TrueThreadLocal
Netty的PoolByteBufAllocator$PoolThreadLocalCache用到了ThreadLocal,如果運(yùn)行在fiber上,每次PoolThreadLocalCache.get()都會返回新的PoolThreadCache對象(因?yàn)槊總€請求起一個新的fiber處理,非WebActor模式)
而在PoolThreadCache的構(gòu)造函數(shù)里,會調(diào)用ThreadDeathWatcher.watch,把當(dāng)前線程和PoolThreadLocalCache.get()返回的對象 add到全局ThreadDeathWatcher列表,以便相關(guān)線程停止的時候能釋放內(nèi)存池
但是對于fiber就會有問題了, PoolThreadLocalCache.get()不斷的返回新的對象,然后add到ThreadDeathWatcher,而正真運(yùn)行fiber的fiber-Fork/JoinPool的worker線程并不會終止,最終導(dǎo)致ThreadDeathWatcher里watcher列表越來越多,導(dǎo)致memory leak,100% full gc time
問題總結(jié):fiber上ThreadLocal返回的對象,逃逸到了全局對象里,而netty只會在真正的線程(os thread)終止時釋放內(nèi)存
解決辦法: 不使用Netty的對象池,或則mock netty代碼換成用TrueThreadLocal
啟動的時候會有[quasar] WARNING: Can’t determine super class of xxxQuasar這個告警只會在啟動的時候出現(xiàn),可以忽略,Quasar暫時沒有開關(guān)可以swith off
Fabio: As for the first warning, this is the relevant code and it basically means Quasar’s instrumentor couldn’t load a class’ superclass. This can happen because the class is not present or, more likely, because the classloader where that instrumentation code is running doesn’t allow to access it. Adding to that the strange warning about the agent not being running, I think the latter is most probably the case.
If the application runs you can just ignore the warnings (they should be printed only at instrumentation time, so bootstrap/warming stage) or if you can share a minimal project setup I could help having a deeper look to figure out what’s happening exactly.
https://groups.google.com/for...
FJP worker運(yùn)行時如果有疑似blocking,會有WARNING hogging the CPU or blocking a threadyou can disable the warning by setting a system property with "-Dco.paralleluniverse.fibers.detectRunawayFibers=false”
獨(dú)立Tomcat + Quasar Agent FiberHttpServlet報(bào)NPE[quasar] ERROR: Unable to instrument class co/paralleluniverse/fibers/servlet/FiberHttpServlet
From the full logs I see that my setup and your setup are different though: I’m using an embedded Tomcat while you’re running Tomcat as a standalone servlet container and using the agent for instrumentation. Unfortunately the agent doesn’t currently work with standalone Tomcat and you need to use the instrumenting loader.
官方推薦:獨(dú)立Tomcat + QuasarWebAppClassLoader 或者 內(nèi)嵌容器 + Quasar Agent
WARNING: Uninstrumented methods on the call stack (marked with **)Quasar不能修改第三方庫為@Suspend, 可以顯式的把相關(guān)的方法放入META-INF/suspendables
獨(dú)立Tomcat + QuasarWebAppClassLoader UnableToInstrumentException (harmless)
這是個Comsat的bug,但是無害,可以忽略
參考資料UnableToInstrumentException: Unable to instrument co/paralleluniverse/fibers/Fiber#onResume()V because of catch for SuspendExecution
google group comsat issues 25
google郵件組
paralleluniverse博客
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/64980.html
摘要:所以系統(tǒng)的吞吐能力取決于每個線程的操作耗時。另外過多的線程,也會帶來更多的開銷。其代表派是以及里的新秀。類似線程也有自己的棧。線程中斷的條件只有兩個,一個是拋異常,另外一個就是。 什么是協(xié)程(coroutine) 這東西其實(shí)有很多名詞,比如有的人喜歡稱為纖程(Fiber),或者綠色線程(GreenThread)。其實(shí)最直觀的解釋可以定義為線程的線程。有點(diǎn)拗口,但本質(zhì)上就是這樣。 我...
摘要:定時器例子之前通過調(diào)用定時器,需要傳一個回調(diào),然后所有的代碼邏輯都包在里面。這里定時器會阻塞在這一行,直到一秒后才會執(zhí)行下面的一行。 之前介紹過quasar,如果你希望在vert.x項(xiàng)目里使用coroutine的話,建議使用vertx-sync。本篇將介紹vertx-sync。 showImg(/img/bVzIsu); 本來打算另起一篇,寫其他方面的東西,但是最近比較忙,就先寫一篇實(shí)...
摘要:什么是協(xié)程大多數(shù)的開發(fā)人員可能對進(jìn)程,線程這兩個名字比較熟悉。但是為了追求最大力度的發(fā)揮硬件的性能和提升軟件的速度,出現(xiàn)了協(xié)程或者叫纖程,或者綠色線程。原理利用字節(jié)碼增強(qiáng),將普通的代碼轉(zhuǎn)換為支持協(xié)程的代碼。 什么是協(xié)程大多數(shù)的開發(fā)人員可能對進(jìn)程,線程這兩個名字比較熟悉。但是為了追求最大力度的發(fā)揮硬件的性能和提升軟件的速度,出現(xiàn)了協(xié)程或者叫纖程(Fiber),或者綠色線程(GreenTh...
摘要:基于的和的,比和之流好看實(shí)在太多了。而且同樣是異步實(shí)現(xiàn)的,應(yīng)該性能不差的。支持多個客戶端同時連接的。這個是配套的文件特別提一下,使用非常方便,直接可以打開的項(xiàng)目。 現(xiàn)代的Java開發(fā)真的和我當(dāng)年認(rèn)識的很不一樣了,這三篇文章非常值得一讀: http://blog.paralleluniverse.co/2014/05/01/modern-java/http://blog.paralle...
閱讀 2067·2023-04-26 01:41
閱讀 2586·2021-11-24 09:39
閱讀 1995·2021-11-24 09:38
閱讀 2038·2021-11-19 09:40
閱讀 3893·2021-11-11 11:02
閱讀 3354·2021-10-20 13:48
閱讀 3348·2021-10-14 09:43
閱讀 4605·2021-09-02 15:11