JDK21虚拟线程

虚拟线程是JDK21最重要的、可能会成为JDK8的lambda这样标志性的特性。这里对虚拟线程做原理级别的系统性分析。

虚拟线程的设计动机

对于后台服务器开发,一个请求对应一条处理线程,是最容易理解的服务器应用编程思路。但是,线程的数量会受到物理资源限制:比如默认Java线程会占用1MB内存,就会受到物理内存大小的限制;比如线程切换涉及内核态-用户态转换,需要频繁保存上下文,浪费CPU时间进行数据复制,受到CPU和带宽的限制。在海量请求到达时,线程资源会很快耗尽,成为主要的性能瓶颈。

早期的一个通常解法是:提高线程的利用率。一方面利用池化技术,减少线程创建销毁的开销,多余任务排队执行减少线程的数量;另一方面利用异步编程API(比如Future),在等待I/O时让出资源。但这样的后果是,一个请求的处理逻辑会被切分成很多段,执行-阻塞-回调-执行-阻塞-回调……这些段可能运行在不同的线程上,导致代码理解困难、调试困难。即使利用JDK8的CompletableFuture对并发编程逻辑做了编排,也只是治标而已。

而解决“线程数量限制”、“线程切换开销大”等问题的一个常用方案,就是构建用户态运行的“协程”。因此,Project Loom项目发起和推进Java的并发编程模型优化,其愿景为“write sync run async”,即“写同步代码,跑异步逻辑”。Loom提出了“纤程(Fiber)“的概念,并认为“纤程”由“续体(Continuation)”和“调度器(Scheduler)”两部分组成。而“纤程”在提供给用户的操作入口,称为“虚拟线程(Virtual Thread)”。“虚拟线程”资源几乎是无限的,每个请求都可以使用一个“虚拟线程”来执行业务逻辑。

阅读更多

HashMap扩容导致的生产问题

HashMap的扩容问题作为Java八股文的重要考点之一,已经背得滚瓜烂熟,但还是在生产中踩了坑(总有一个坑的形状适合你)。这里再记录一下当时的复盘。

问题概述

2023.10.27,某国0点开始,业务投放量同比前一天出现明显下跌(约30%),当天下午通过报表数据发现了此问题,晚上20:00修复代码后数据恢复。

问题发现过程

  • 11:00,下游业务触发限流,发现流量上涨了一倍多,但因临近大促以为是大促流量,直接调高了限流阈值,没有引起重视
  • 16:20,算法同学发现业务投放量有明显下跌,开始排查问题,看到是从0点开始,业务投放量同比昨天下跌了30%左右。排查入口流量,发现流量平稳,并没有大促带来的流量,说明可能是代码问题,排查陷入僵局
  • 19:00~20:00,找到问题原因,修复代码并发布
阅读更多

动态代理调用的实际运用

动态代理在实际工作中很难用到,通常都是一些底层组件才会使用,比如SpringAOP。但由于业务正在做“降本增效”的多租户改造,因此正好有了使用机会。

我们的业务分布在6个国家,每个国家都有独立的服务器、数据库、中间件,然而每个国家的用户数、使用APP时间段、使用习惯等各种因素导致服务器资源的使用效率不高:有的服务可能CPU在1%~10%使用率,为了高可用却仍然需要至少4台服务器,资源会有浪费。现在要做的就是把业务、服务器、数据库均合并,通过全链路携带“租户标记TenantId”来区分请求来源,所有国家共用服务器和数据库资源。

实现的方式很简单,类似skywalking这种tracing组件,利用ThreadLocal等数据结构,将类似“业务_国家_语言_货币单位”这种请求标记全链路传递。

阅读更多

MQTT3.1.1协议解析

协议产生背景

MQTT (Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于TCP/IP网络协议栈的应用层协议。MQTT最开始是1999年IBM公司用于通过卫星通信连接石油管道监测系统而创造的协议。
这种场景的特点是:

阅读更多

百度开源MqttBroker:BifroMQ分析

2023.07.17收到了百度开源IoT Broker的好消息,一直没有时间看。最近工作闲下来了,准备分析一下它的实现。

2019年开发Broker时,调研了很多实现方式,百度IoT是我特别想参考的实现,只可惜当时并没有开源代码可以参考,通过对实现方式的猜测,总结了这篇文章:《百度IoT:MQTT Broker架构设计》,不过现在开源版本改动了很多东西。

我们以BifroMQ v1.0.2版本来分析。

阅读更多

Fastjson的$ref在接口参数兼容上的隐患

oldclass_newclass.jpg
假设应用1给应用2提供了一个接口,需要更新参数,将Map变为List<Map>,很容易写出这样的兼容代码:

1
2
3
4
5
6
7
8
9
10
11
@Data
public static class OldClass {
private Map<String, String> bbbb;
}

@Data
public static class NewClass {
private List<Map<String,String>> aaaa;
@Deprecated
private Map<String, String> bbbb;
}

然而在部署后发现,应用2拿到的数据对象中的bbbb,没有任何数据。

阅读更多

使用ChatGPT和StableDiffusion给Hexo文章添加封面图

最近想添加封面图,想尝试使用StableDiffusion的AIGC自动生成封面图。但是,国内chatGPT很容易被封,像DALL·E这种又没法注册,国内百度的文言一格等未开放API,所以只有使用开源大模型了。

类似github管理代码仓库一样,比较有名的管理开源大模型的网站主要有2个:

考虑到第一次使用,学习曲线需要比较平缓才好入门,我使用国内的ModelScope来做大模型HelloWorld。最终决定使用阿里达摩院提供的中文StableDiffusion-通用领域这个大模型进行生成。

阅读更多

近期对chatGPT的理解

一、和之前“人工智障”的差异在哪里

1、能联系上下文,并不是一问一答,而是连续对话。

天猫精灵这类初代AI,只能做“天气如何”-“今天的天气是”,“今天几号”-“今天是”,这样一问一答的对话。但是一次问答通常只能“查询属性”、“执行命令”这种简单操作,人类的任务通常更复杂,需要分析对方答案并再次提出问题,直到双方观点对齐。chatGPT能够分析上下文,提供了连续问答的能力,所以现在有很多玩法是,让chatGPT扮演某个角色,然后以角色身份进行交互。

2、能够自我纠错,“你这里错了”,且具有主见,“我认为没有错”。

在连续对话的能力下,我们可以指出chatGPT答案中的错误,chatGPT会分析自己答案中的错误,并以更大的正确概率去修正答案。为什么通过指出错误能得到更正确的答案?目前的主流观点认为,这样做相当于把大任务划分为了小任务,单步拆解能给到更好的提示,辅助下一步的推理形成良性循环,因此最终大任务也具有更高的正确率了。

阅读更多

可过期的积分系统

很多产品都会有积分系统:用户通过一定的行为累积积分,积分可以兑换各种权益,积分可能
会有增加(获取)、扣减(消耗)、查询余额、查看历史、过期、冻结等行为。

需求分析

1、积分过期的两种场景

积分过期是一种很常见的产品功能,因为一般都不希望用户累计大量积分,造成预算不可控。
过期有2种方式:

  • 统一时间过期:比如今年所有获得的积分,都在今年年末过期
  • 单笔时间过期:比如“A渠道获取的积分,3个月后过期”、“B渠道获取的积分,7天后过期”。
阅读更多

预算、疲劳度通用设计

业务场景分析

通常业务都有控制预算或疲劳度的需求:

  • 预算:每天最多发放N张优惠券、每月最多发放价值1000元的金币,……
  • 疲劳度:每小时最多曝光N次,每个用户每天最多展示N次,……
    本质抽象出来,就是属于“限流”中的“计数器模型”,在指定时间段内,请求数量累加,有数量的最大限制。

整体思路分析

budget_fatigue_model.jpg

阅读更多