<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
趁著國慶前後閱讀了虛擬執行緒相關的原始碼,寫了一篇《虛擬執行緒 - VirtualThread原始碼透視》,裡面介紹了虛擬執行緒的實現原理和使用範例。需要準備做一下前期準備:
OpenJDK-19
或者Oracle JDK-19
Tomcat
的依賴,需要引入三個依賴包,分別是tomcat-embed-core
、tomcat-embed-el
和tomcat-embed-websocket
,版本選用10.1.0+
檢視Tomcat
官方檔案的CHANGELOG
:
支援Loom
專案的Tomcat
最低版本為10.1.0-M16
,對應的正式版是10.1.0
(當前時間為2022-10-07
前後),低於此版本因為大量API
還沒有適配虛擬執行緒,主要是沒有改造監視器鎖的參照導致虛擬執行緒pin
到載體(平臺)執行緒等問題,因此別無他選。另外,重要的提醒說三次:
引入以下依賴:
<dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>10.1.0</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> <version>10.1.0</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>10.1.0</version> </dependency>
為了使用反射呼叫一些java.base
模組下沒開放的依賴包和跟蹤虛擬執行緒棧,程式執行時候加入下面的VM
引數:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED -Djdk.tracePinnedThreads=full
在IDEA
的執行設定中是這個樣子:
接著編寫一個HttpServlet
實現:
public class VirtualThreadHandleServlet extends HttpServlet { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Thread thread = Thread.currentThread(); System.out.printf("service by thread ==> %s, is virtual ==> %s, carrier thread ==> %sn", thread.getName(), thread.isVirtual(), getCurrentCarrierThreadName(thread)); resp.setStatus(HttpServletResponse.SC_OK); resp.setHeader("Content-Type", "application/json"); String content = "{"time":" + """ + LocalDateTime.now().format(FORMATTER) + ""}"; resp.getWriter().write(content); } private static String getCurrentCarrierThreadName(Thread currentThread) { if (currentThread.isVirtual()) { try { MethodHandle methodHandle = MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup()) .findStatic(Thread.class, "currentCarrierThread", MethodType.methodType(Thread.class)); Thread carrierThread = (Thread) methodHandle.invoke(); return carrierThread.getName(); } catch (Throwable e) { e.printStackTrace(); } } return "UNKNOWN"; } }
該Servlet
實現比較簡單,就是在控制檯列印一些虛擬執行緒和載體執行緒的一些資訊,然後返回HTTP
狀態碼為200
和一個JSON
字元展示當前精確到毫秒的時間。接著編寫一個main
方法初始化Tomcat
:
public class EmbedTomcatVirtualThreadDemo { private static final String SERVLET_NAME = "VirtualThreadHandleServlet"; private static final String SERVLET_PATH = "/*"; /** * 設定VM引數: * --add-opens java.base/java.lang=ALL-UNNAMED * --add-opens java.base/java.lang.reflect=ALL-UNNAMED * --add-opens java.base/java.util.concurrent=ALL-UNNAMED * -Djdk.tracePinnedThreads=full * * @param args args * @throws Exception e */ public static void main(String[] args) throws Throwable { String pinMode = System.getProperty("jdk.tracePinnedThreads"); System.out.println("pin mode = " + pinMode); Tomcat tomcat = new Tomcat(); Context context = tomcat.addContext("", (new File(".")).getAbsolutePath()); Tomcat.addServlet(context, SERVLET_NAME, new VirtualThreadHandleServlet()); context.addServletMappingDecoded(SERVLET_PATH, SERVLET_NAME); Connector connector = new Connector(); ProtocolHandler protocolHandler = connector.getProtocolHandler(); if (protocolHandler instanceof AbstractProtocol<?> protocol) { protocol.setAddress(InetAddress.getByName("127.0.0.1")); protocol.setPort(9091); ThreadFactory factory = Thread.ofVirtual().name("embed-tomcat-virtualWorker-", 0).factory(); Class<?> klass = Class.forName("java.util.concurrent.ThreadPerTaskExecutor"); MethodHandle methodHandle = MethodHandles.privateLookupIn(klass, MethodHandles.lookup()) .findStatic(klass, "create", MethodType.methodType(klass, new Class[]{ThreadFactory.class})); ExecutorService executor = (ExecutorService) methodHandle.invoke(factory); protocol.setExecutor(executor); } tomcat.getService().addConnector(connector); tomcat.start(); } }
這裡VirtualThreadHandleServlet
匹配所有格式的請求路徑並且處理所有請求方法型別的請求。預設的虛擬執行緒排程器沒有為虛擬執行緒設定名稱,也就是如果使用Executors.newVirtualThreadPerTaskExecutor()
作為Tomcat
的執行緒池是最終呼叫看到的控制檯輸出的虛擬執行緒名稱是一個空字串。所以筆者這裡用MethodHandle
直接範例化了預設修飾符沒有開放存取許可權的ThreadPerTaskExecutor
類,基於一個自定義的ThreadFactory
強制構造了一個自定義ThreadPerTaskExecutor
範例。呼叫main
方法啟動後見控制檯輸出:
這裡確認了Tomcat
啟動完成偵聽127.0.0.1:9091
,通過瀏覽器或者POSTMAN
傳送任意請求例如http://127.0.0.1:9091/foo
就能看到響應結果和控制檯輸出:
這裡的Tomcat
執行緒池甚至可以設計為一個完全自定義的虛擬執行緒排程器,可以參考前面一篇文章,這裡不再贅述。
由於Servlet
規範問題,Tomcat
的升級導致一些介面遷移到jakarta.servlet
包中,例如jakarta.servlet.Servlet
,此時SpringBoot
體系即使是最新版本(當前時間為2022-10-07
前後,此時最新版本為2.7.4
)使用的是還是舊的規範,對應的類是javax.servlet.Servlet
,這只是其中一個介面,大部分和HTTP
協定或者Servlet
規範相關的介面都存在這個包升級不相容的問題,需要等待SpringBoot
升級為embed-tomcat-*-10.1.0+
才能適配虛擬執行緒。
Demo
專案倉庫:
Github
:https://github.com/zjcscut/framework-mesh/tree/master/tomcat-virtual-thread
到此這篇關於在Tomcat中啟用虛擬執行緒特性的文章就介紹到這了,更多相關Tomcat啟用虛擬執行緒內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45