老树新花-Java异步服务开发

简介: 饿了么资深Java工程师朱杰从同步异步概念介绍、使用Java来开发异步化服务、回调监听模式所遇到的问题和解决这三方面来我们全面解读Java异步服务开发。

u_1705419240_2440941745_fm_214_gp_0

内容来源:2017年5月13日,饿了么资深Java工程师朱杰在“Java开发者大会 | Java之美【上海站】”进行《老树新花-Java异步服务开发》演讲分享。IT大咖说作为独家视频合作方,经主办方和讲者审阅授权发布。
阅读字数: 1901 用时: 13分钟

嘉宾演讲视频地址:http://t.cn/RKtxNEE

同步模型

以前在并发量很低的情况下,是通过线程去收取数据并发送数据给客户端。但是当并发量和客户端连接数比较高的时候,服务器会出现明显的瓶颈。

1

阻塞模型比较符合人的思考逻辑,但它会有线程阻塞的问题。阻塞模型会让出CPU,不适用于高并发。

线程池或连接池只能解决一部分问题。因为线程池和连接池的本质作用并不是能直接提高QPS,而是减少或销毁线程的连接处以及开销。

我们平时调用阻塞API的一个问题就在于单机的线程数是有限的。所以如果要提高服务端性能的话,首先就要去阻塞。

异步模型

异步阻塞模型处理I/O时大部分时间是非阻塞的(监听时除外),它调用的API会立即返回,这点是需要注意的。此外,处理结果的程序并不保证调用API当前的线程,这点在处理线程安全的问题上尤其要注意。

Java中很多API都是基于操作系统底层API的模型,并没有做更高层次的封装。

Java把一层阻塞异部I/O做了封装,这些就是Java或C语言异步模型的基石。

少数线程等待事件发生,再根据对应类型处理相关事件。

2

最近“协程”这个词比较火,看上去能解决异步模型的大部分问题。它是一个轻量级线程,可以直接当作线程来用。还能阻塞I/O API,阻塞的是协程而非线程。

协程是用户态资源,用户态调度,消耗极低,可以启动数十万个协程。

它的实现和线程不是1对1 的关系,难点在于编程语言的内部实现。

Python虽然支持协程,但是由于全局解释锁的关系,同一时刻只有单个CPU在运行。所以python选择多进程+协程的做法。

Go语言完美解决了支持不阻塞线程的I/O操作,并支持多线程。

要能像同步I/O一样编写代码,不会创造过多数量的线程。尽量让CPU处于忙碌状态而非等待,并寻找满足以上条件的Java库。但是Java由于本身语言的问题,即使是Java协程三方库也只能部分支持协程。

退而求其次,我们只能使用Java异步工具库。如果要提高并发量,可以使用异步JDBC和异步HTTP CLIENT,这个库基于NETTY。

做到服务异步化,要查看接口是否可支持异步。还可以使用Java的异步工具库,比如Java的异步数据访问方式和异步HTTP CLIENT。如果使用的是三方框架,可以修改调用方式,有的框架支持异步回调和事件监听。最重要的是要注意线程安全问题。

异步化的优势就是极大提高了I/O密集型业务的性能,保守估计有10~100倍,也就解决了线程数创建过多的问题。

而它的缺点是增加了编程难度,包括状态保存、回调处理以及线程安全等。也存在压垮下游服务的问题:)

老树新花-基于Netty的Java模型

Netty是基于原生的异步模型,封装并优化。它修复JDK中的一些BUG,提供了多种辅助类方便开发。编程模型高效简单,开发者只需关心具体实现逻辑即可,基本不用花精力做Java网络层面的优化。此外,Netty成熟稳定,业界使用多,我们能够相信,使用它不会遇到难以解决的大问题。

Netty基于事件连接,如果有数据传入以及连接上有异常事件或自定义事件,只需复写它的回调函数就能做相应的处理。

Netty所有I/O API全都是消除阻塞异步化。线程模式也非常好,它单个连接上的所有I/O事件都由同一个线程执行,避免了线程安全问题。

3

Netty的单个链接绑定一个线程,EVENTLOOP即一个线程,EVENTLOOPGROUP是一个线程组。

Netty任何的I/O API都是产生一个任务,放入该连接对应的线程里执行,做到局部串行化。

Netty一切操作都是以事件驱动来执行,所有I/O API都是用异步+回调监听的方式来处理消息。单一的连接处理都在一个线程里,来避免线程安全问题。

案例-饿了么数据库中间件

我们是一个实现了MYSQL协议的中间代理服务。上游至少要支持上万客户端的连接,下游要支持上千数据库连接。

为了快速实现功能,我们最早是找了一个基于GITHUB阻塞I/O开源库,首要任务是把同步改为了异步。

线程模型的上下游各有Netty的一个线程组,中间件内部还有一个处理业务的线程组。

4

但有一个最本质的问题在于这三个线程组之间的线程安全得不到保证。

因为这个中间件前后端都是异步的,所以按正常流程是由协议来保证顺序执行,而异常中断是并发执行的。

参考Netty,我们把每一个连接绑定到一个单线程池,保证Task串行执行。

5

前后端异步的好处在于模型简单,便于后续修改。

我个人认为,在程序里过多的使用WAIT/NOTIFY/LOCK不一定代表良好的多线程编程能力,却可能代表这是不够优雅的设计。

6

除了前后端异步和信号量控制异步,在中间件中我们还用到了日志异步、心跳异步、JOB异步和配置变更异步。

在饿了么数据库中间件开发过程中,异步化所有API以去除阻塞,局部串行化解决线程安全问题,模型简单,易于修改和理解。

今天的分享就到这里,谢谢大家!
_

目录
相关文章
|
2天前
|
Java Maven Windows
使用Java创建集成JACOB的HTTP服务
本文介绍了如何在Java中创建一个集成JACOB的HTTP服务,使Java应用能够调用Windows的COM组件。文章详细讲解了环境配置、动态加载JACOB DLL、创建HTTP服务器、实现IP白名单及处理HTTP请求的具体步骤,帮助读者实现Java应用与Windows系统的交互。作者拥有23年编程经验,文章来源于稀土掘金。著作权归作者所有,商业转载需授权。
使用Java创建集成JACOB的HTTP服务
|
13天前
|
Java
探索Java新境界!异步+事件驱动,打造响应式编程热潮,未来已来!
【8月更文挑战第30天】在现代软件开发中,系统响应性和可扩展性至关重要。Java作为主流编程语言,提供了多种机制如Future、CompletableFuture及事件驱动编程,有效提升应用性能。本文探讨Java异步编程模型与事件驱动编程,并介绍响应式模式,助您构建高效、灵活的应用程序。
30 3
|
14天前
|
小程序 JavaScript Java
【Java】服务CPU占用率100%,教你用jstack排查定位
本文详细讲解如何使用jstack排查定位CPU高占用问题。首先介绍jstack的基本概念:它是诊断Java应用程序线程问题的工具,能生成线程堆栈快照,帮助找出程序中的瓶颈。接着,文章通过具体步骤演示如何使用`top`命令找到高CPU占用的Java进程及线程,再结合`jstack`命令获取堆栈信息并进行分析,最终定位问题代码。
96 1
【Java】服务CPU占用率100%,教你用jstack排查定位
|
3天前
|
Java 数据库连接 数据库
Java服务提供接口(SPI)的设计与应用剖析
Java SPI提供了一种优雅的服务扩展和动态加载机制,使得Java应用程序可以轻松地扩展功能和替换组件。通过合理的设计与应用,SPI可以大大增强Java应用的灵活性和可扩展性。
22 11
|
8天前
|
数据采集 Java 数据挖掘
Java IO异常处理:在Web爬虫开发中的实践
Java IO异常处理:在Web爬虫开发中的实践
|
10天前
|
数据采集 存储 前端开发
Java爬虫开发:Jsoup库在图片URL提取中的实战应用
Java爬虫开发:Jsoup库在图片URL提取中的实战应用
|
13天前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
11天前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
50 0
|
11天前
|
开发者 C# 自然语言处理
WPF开发者必读:掌握多语言应用程序开发秘籍,带你玩转WPF国际化支持!
【8月更文挑战第31天】随着全球化的加速,开发多语言应用程序成为趋势。WPF作为一种强大的图形界面技术,提供了优秀的国际化支持,包括资源文件存储、本地化处理及用户界面元素本地化。本文将介绍WPF国际化的实现方法,通过示例代码展示如何创建和绑定资源文件,并设置应用程序语言环境,帮助开发者轻松实现多语言应用开发,满足不同地区用户的需求。
22 0
|
11天前
|
Java 数据库连接 数据库
AI 时代风起云涌,Hibernate 实体映射引领数据库高效之路,最佳实践与陷阱全解析!
【8月更文挑战第31天】Hibernate 是一款强大的 Java 持久化框架,可将 Java 对象映射到关系数据库表中。本文通过代码示例详细介绍了 Hibernate 实体映射的最佳实践,包括合理使用关联映射(如 `@OneToMany` 和 `@ManyToOne`)以及正确处理继承关系(如单表继承)。此外,还探讨了常见陷阱,例如循环依赖可能导致的无限递归问题,并提供了使用 `@JsonIgnore` 等注解来避免此类问题的方法。通过遵循这些最佳实践,可以显著提升开发效率和数据库操作性能。
29 0