欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

《支持JDK19虚拟线程的web框架》系列文章链接

本篇概览

  • 本篇是《支持JDK19虚拟线程的web框架》系列的第五篇,也是全系列的终篇,之前的文章实战、写代码、读源码,想必把大家累坏了,今天咱们开启聊天模式,畅谈虚拟线程中的一个关键问题,在轻松的气氛中学习知识,也为整个系列顺利收官

关于ThreadLocal

  • 既然提到了线程,自然绕不开ThreadLocal类,它提供了线程本地变量,此变量和一般的变量不同。通过get & set 方法,每个线程可以获取到自己独立的变量。这个变量实例通常是私有且静态的,可以存储与线程相关的信息,如产品id、事务id等。

  • 下图很形象的展现了ThreadLocal:是完全属于每个线程自己的集合

虚拟线程中,ThreadLocal的问题

  • 既然每个线程都可以拥有属于自己的ThreadLocal对象,那虚拟线程的情况又如何呢?
  • 虚拟线程的特性,使得我们可以在应用代码中创建成千上万个虚拟线程去执行并发任务,而无需担心线程数量对整体计算资源的负担,如果每个线程都用了ThreadLocal,那会不会出现成千上万的ThreadLocal对象呢?线程是虚拟的,对象可是实实在在的,这样会增加系统资源消耗,或者影响性能吗?
  • 不过转念一想,这么明显的问题,连我都能想到,JDK组织又岂会漏掉?应该是我多虑了吧,凭自己"丰富的经验",我预测解决方案应该和TLAB(Thread Local Allocation Buffer)类似,为海量虚拟线程的ThreadLoacal对象建立映射关系,做到高效管理
  • 然而现实很残酷,脸,被狠狠地抽打,通过Oracle官方博客,知道实际情况真惨...,如下图,中文注释是我的解读,极具悲观色彩,如果翻译得不准确请您告知,谢谢

  • 对上述内容,个人理解是以下两点:
  1. 虚拟线程中使用ThreadLocal确实会带来内存问题,现在还无解,连虚拟线程自身的工程Loom都在自己代码中删除ThreadLocal的使用,那么我们普通用户敢用吗?还是避而远之吧,在虚拟线程中不要用ThreadLocal
  2. 编号429的JEP,为我们带来了一个解决方案,一种名为Scoped values的变量,可以在一定范围(scope)内被访问,至于这个scope,可以是一个内存范围(例如临时变量就只能在方法内),另外还有一种范围被称为dynamic scope,这个范围就更加灵活了,不过这个JEP当前的状态还很早期,如下图,还在提案阶段,这要是跳票了或者被否了,那我博客不就白写了?就此打住吧,我不能再研究了

  • 搞清楚以上问题后,自己的八卦之心就控制不住了:既然虚拟线程上的ThreadLocal问题这么严重,放眼Java世界的生态这么繁荣,那么多框架和库,那么...你们说
  1. 有没有哪个倒霉蛋掉进这个坑里去?

  2. 惨不惨?

  3. 从坑里爬出来没有?

  • 你别说,还真有...

踩坑勇士quarkus

  • 这位踩坑勇士,就是贯穿整个《支持JDK19虚拟线程的web框架》系列的quarkus,来吧,一起围观quarkus踩坑,顺便学点知识
  • 先看quarkus官方文档《virtual-threads.adoc》,如下图

  • 我对上述内容的理解:
  1. quarkus的人发现:传统线程池模式改用虚拟线程后,性能提升明显,但是反应式框架改用虚拟线程后的提升并不明显,而且还会带来内存消耗过大的问题(看过前面ThreadLocal分析的您,此刻应该猜到原因了了,嘿嘿,您猜的没错)
  2. 如果您的应用对内存有较严要求,quarkus官方建议您继续坚持(stick)使用反应式框架(这话中透露出浓浓的无可奈何,别催了,搞不定...)
  • 接下来官方就要甩锅了,有趣的是,这次接锅的并非JDK,而是大名鼎鼎的...Netty

Netty的问题

  • 为什么是Netty接锅呢?
  • 首先,Netty使用了Reactor线程模型,而Netty Reactor的核心是Event Loop,下图来自《Netty in Action》,是处理web请求的内部架构图,

  • 那么,应该有多少个EventLoop线程呢?下图是Netty源码,默认值是CPU核数的2倍,看得出这是个很保守的数字

  • 从上面的架构图和代码可以看出,Netty的反应式框架的核心是使用少量线程来分发web请求,这样的结果仅使用了少量线程资源就能高效处理事件
  • 也正式因为有了线程数不多这个前提,在对JSON做序列化处理时,Netty放心的使用了ThreadLocal,毕竟线程少,一个4核的CPU也才8个ThreadLocal,毫无压力
  • 而且,为了更加高效,Netty还对ThreadLoacal进行过改造,也就是他们自研的FastThreadLocal
  • 然后,时间一天天过去,终于等来了JDK19发布,
  • quarkus的反应式web服务模块底层就是Netty,为了用上虚拟线程,他们动手了...咱们脑补一下吧,铺天盖地的虚拟线程线程,铺天盖地的FastThreadLocal对象,炸了吧您...Are U OK ?
  • 快乐之后,咱们还是要正视这个问题,表面上看是个坑,实际上是两种设计思路的冲突:
  1. 虚拟线程的特性类似golang的协程,很适合直接拿来处理高并发web请求,为每个请求分配一个虚拟线程,逻辑清晰直白,资源消耗又不高,典型的简单高效
  2. Netty的反应式模型,核心思路就是用少量线程高效分发大量请求,本身就很高效,而且就算优化,线程数也不是瓶颈
  3. 所以,quarkus拎着虚拟线程冲到Netty的地盘一阵操作猛如虎,一看结果...唉,扯远了,来看quarkus官方的解释吧

  • 上图红框中那句话很有价值,咱们都能从中领悟到一些东西,我的收获是:当线程数不是系统瓶颈的时候,就别冲动,强行上虚拟线程没用

quarkus强行挽尊

  • 既然虚拟线程不适合反应式模型,个人认为:那就不妨大大方方的承认Netty的Reactor是优秀的,放弃将虚拟线程加入进来,这样不是挺好么?
  • 然而quarkus接下来的操作还是把我吓到了:既然虚拟线程不适合反应式模型?那就想办法强行让它适合,下图就是quarkus的做法:在构建阶段,找到创建ThreadLocal的那段代码,修改它的字节码,以此来解决前面的内存问题

  • 然后我就翻到了上图提到的那段代码

  • 好奇心驱使,我点开上图那个NettyCurrentAdaptor去看了下源码,当时就一阵头晕眼花,ASM风格的代码您能撑多久?试试下图

  • 按照官方的说法,经过他们的优化有百分之八十的提升,终于快要达到之前反应式框架的水平了
  • 呃,搞得这么辛苦,也只是快要追上而已,那行,咱不用了行吗?
  • 另外,上面说的优化手段也不是默认开启的,还要做以下几步操作
  1. maven的pom.xml添加以下依赖
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-netty-loom-adaptor</artifactId>
</dependency>
  1. 编译构建的时候,增加参数-Dnet.bytebuddy.experimental

  2. 启动的时候,增加参数--add-opens java.base/java.lang=ALL-UNNAMED

  • 上述操作算,quarkus的手段,我这个草根只能仰望,能开拓自己的见识:原来还可以这样解决问题
  • 但我自己是绝对不敢模仿的,开玩笑,在编辑阶段注入代码,难度太大,并且后面如何维护和交接?

小结

  • 至此,咱们压测做了,代码写了,源码读了,八卦也看了,《支持JDK19虚拟线程的web框架》系列也到了和您说再见的时候
  • 虚拟线程很诱人,欣宸和您一样,迫不及待的想在实际项目中将其用上,实实在在的解决一些问题,正是有了这个目标,才促进了《支持JDK19虚拟线程的web框架》系列的诞生,本着为我所用的心态去学习、了解、模仿、钻研,希望在虚拟线程发布的早期阶段,该系列文章能丰富您的知识面,为您的决策提供参考信息,助您在掌握新技术的时候顺利抢占先机
  • 系列虽然结束了,欣宸原创不会停止,这里永远是咱们Java爱好者的宁静港湾,欢迎您的关注

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

支持JDK19虚拟线程的web框架,之五(终篇):兴风作浪的ThreadLocal的更多相关文章

  1. 支持JDK19虚拟线程的web框架,之二:完整开发一个支持虚拟线程的quarkus应用

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本篇是<支持JDK19虚拟线程的web ...

  2. 支持JDK19虚拟线程的web框架,之一:体验

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于虚拟线程 随着JDK19 GA版本的发布,虚拟线程 ...

  3. 支持JDK19虚拟线程的web框架之四:看源码,了解quarkus如何支持虚拟线程

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 前文链接 支持JDK19虚拟线程的web框架,之一:体 ...

  4. Web框架django基础篇

    基本配置及学习  路由(Urls).视图(Views).模板(Template).Model(ORM). 简介 Django 是一个由 Python 写成的开放源代码的 Web 应用框架.它最初是被开 ...

  5. Web框架之Django篇

    1.创建Project 命令: django-admin startproject mysite 2.配置 (1)模板配置 (2)静态文件配置 (3)csrf隐藏 3.路由关系 一个url对应一个函数 ...

  6. Web框架django进阶篇

    分页 一.Django内置分页 from django.shortcuts import render from django.core.paginator import Paginator, Emp ...

  7. 150行代码搭建异步非阻塞Web框架

    最近看Tornado源码给了我不少启发,心血来潮决定自己试着只用python标准库来实现一个异步非阻塞web框架.花了点时间感觉还可以,一百多行的代码已经可以撑起一个极简框架了. 一.准备工作 需要的 ...

  8. 两个基于C++/Qt的开源WEB框架

    1.tufao 项目地址: https://github.com/vinipsmaker/tufao 主页: http://vinipsmaker.github.io/tufao/ 介绍: Tufão ...

  9. python web应用--web框架(三)

    了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应. 但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL. 每一个URL ...

随机推荐

  1. 使用man手册查看内核函数

    1.为避免man的一些功能缺失,先装好manpages-dev sudo apt-get install manpages-dev 2.安装mandocs的依赖包xmlto sudo apt-get ...

  2. KingbaseES sys_blocking_pids 函数

    会话出现了锁等待,想要快速查询到堵塞的会话,可以使用 sys_blocking_pids 函数来实现这一目的. sys_blocking_pids:获取哪些会话阻塞了某个会话(输入参数). sys_b ...

  3. 如何在Windows中查询证书颁发机构已颁发的证书

    有时候需要看一下证书颁发机构已经颁发出去的证书,看看某个用户或者某个计算机获取过的证书有哪些.通常可以在证书颁发机构的MMC中查看.对于测试环境或者刚开始用的CA来说,这样查看挺简单的.但是对于用了一 ...

  4. .NET Core Web APi类库如何内嵌运行?

    话题 我们知道在.NET Framework中可以嵌入运行Web APi,那么在.NET Core(.NET 6+称之为.NET)中如何内嵌运行Web Api呢,在实际项目中这种场景非常常见,那么我们 ...

  5. 第六章:Django 综合篇 - 11:分页 Paginator

    分页功能是几乎所有的网站上都需要提供的功能,当你要展示的条目比较多时,必须进行分页,不但能减小数据库读取数据压力,也有利于用户浏览. Django又很贴心的为我们提供了一个Paginator分页工具, ...

  6. Kubernetes 控制器

    在实际使用的时候并不会直接使用 Pod,而是会使用各种控制器来满足我们的需求,Kubernetes 中运行了一系列控制器来确保集群的当前状态与期望状态保持一致,它们就是 Kubernetes 的大脑. ...

  7. Grafana 入门知识介绍

    通过[Configuration]>[Plugins]添加插件 通过[Configuration]>[Data Sources]添加数据源(分析对象) 通过[Server Admin]&g ...

  8. 【Azure 应用服务】App Service频繁出现 Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener 异常分析

    问题描述 在使用App Service的过程中,发现应用频繁出现503错误,通过Kudu站点获取到Logfiles. 在 Eventlog.xml 文件中,发现大量的 Microsoft.Window ...

  9. NSIS限制程序运行次数和使用日期

    #七八年前写着玩的小东西,实际用途不大,但对刚接触nsis的新手来说应该还有一些帮助,包括创建控件,获取系统时间等,与诸位共勉! !system '>blank set/p=MSCF<nu ...

  10. DDD-领域驱动(二)-贫血模型与充血模型

    贫血模型 一般来说 贫血模型:**一个类中只有属性或者成员变量,没有方法 **!例如 DbFirst 从数据库同步实体过来, -- 对于一个系统刚开始的时候会觉得这时候是最舒服的,但是如果后期系统需要 ...