▶关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描下方的二维码~。
定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。
如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。
如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。
如果第二次看到我的文章,欢迎「左侧导航栏」或「文末」扫码订阅我个人的公众号(跨界架构师)哟~
每周五早8点 按时送达到公众号。当然了,也会时不时加个餐~
Z哥在前面的三篇文章里和你一起聊了「高性能」主题下与「缓存」相关的内容。这次和你来聊聊提高性能的另一个大招——「异步」。
如果你已经对「异步」有所了解的话,这次可以让你有更深刻的理解。如果你对「异步」的了解比较模糊的话,这次可以带你一次性深入浅出。
「异步」有啥用?
不管我们的思维模式也好,还是平时写的最习惯的代码,其实都是以「同步」的方式在进行的。所以,「同步」方式用着也挺好,为啥要「异步」呢?拿你平时去买奶茶、买咖啡的例子来说说你就明白了。
你应该有注意到,一般奶茶店都会分“点单区”和“取餐区”。
然后你去消费的时候都是在“点单区”选择饮料然后付钱,在“取餐区”拿做好的饮料。其实这个过程就是「异步」的,因为当营业员在做饮料的时候,你是可以去干其它事的,比如在边上开一局王者荣耀或者吃鸡。而他们也可以继续接受后面顾客的点单。
如果是「同步」会怎样呢?就是你在点单区点好饮料之后,继续排着队干等着营业员做好,直到营业员把饮料做好交给你之后,你就可以走人了,他再继续服务后面的顾客。
很明显,如果一个店铺里有2个或者2个以上的营业员,用这种「异步」的方式“吞吐量“更高。
因为来买饮料的人时间是不规律的,可能有时候一下子来十几个,可能有时候半小时都不来一个。那么通过这种「异步」的方式,虽然不能缩短制作饮料的时间,但是可以缩短人流量大的时候顾客的等待点单时间,让顾客可以去做其它事。
其实软件系统也是如此,如今我们程序所在的服务器几乎全部是多核多线程的。既然有多个“营业员”在,那么通过「异步」的方式尽可能的发挥多线程的作用,才是物尽其用的办法,还能提升整体的效率。
不过,这事在软件系统中要稍微复杂一些,要多考虑一下。因为线程的创建、销毁、切换成本在很多时候甚至比获得的收益还要高。所以,只有将「异步」运用于「等待处理的时间」>「创建、销毁、切换线程的时间」的场景下才有价值。
要满足这种场景的话,一般就是涉及到「I/O」处理的地方。比如磁盘I/O、网络I/O。
比如,一旦涉及到数据库查询或者RPC调用的时候,如果使用「同步」的方式通信,发起一个调用后,调用方会阻塞自己并等待整个操作的完成(想象一下执行一条耗时10秒钟的sql)。如果使用「异步」通信的话,调用方不需要等待操作完成就可以返回,甚至可能不需要关心整个操作完成与否。
特别对于如今的移动网络环境下,通过异步的方式可以在很大程度上保证当网络很卡的时候APP上的操作依然是流畅的,不会出现卡机。
同步vs异步
任何事物都是有利有弊的。「同步」可以立马知道到底成功与否(比如做奶茶的时候营业员发现珍珠没了,马上就可以告诉你),而「异步」不行(这个时候你可能去别的地方溜达了)。这也导致了在「异步」环境下做「事务」的成本更高。
而且,在分布式系统中遍布着RPC调用,如果是「同步」调用的话,还可以配合「短链接」做到对连接资源的用完即放。而「异步」的话连接保持的时间要更长一些,至少要等到回调触发完成后才能释放。
而且Z哥还要提醒你,在使用「异步」的时候,有两点特别容易被忽略。
-
发起请求的线程往往和接收响应的线程不是同一个,所以「线程上下文」是不连续的。(当然可以通过做一些额外的编码工作达到类似的效果)
-
虽然请求的顺序是由客户端控制的,但是回调的时候可能就不一定是按照请求时的顺序进行的,像下图这样。
这么看来,「同步」和「异步」都可以通过「请求/响应」模型来完成。但是,「异步」在跨进程通讯中更合适抽象成「事件」来进行协作。
通过「事件」进行「异步」协作的话,客户端不是发起请求,而是发布一个「事件」,然后期待其他的协作者接收到该消息,并且知道该怎么处理它,客户端不用关心其他协作者做了什么,甚至也无需知道有哪些协作者存在。
基于「事件」的协作方式耦合度很低。客户端发布一个「事件」,但并不需要知道谁或者什么会对此作出响应,这也意味着,你可以在不影响客户端的情况下对该「事件」添加新的订阅者。
总的来说,异步虽然能提升效率,但是还是无法在所有场景使用它。在实际工作中,往往我们会同时运用「同步」和「异步」,所以了解清楚它们之间的区别和优缺点是很有必要。
怎么做异步?
我们以一个电商APP中的“下单”场景来举个例子。
在电商的业务场景中,下单最常见的就是以下几个操作(顺序随便排的)。
-
扣减库存
-
核销优惠券
-
生成订单
-
生成电子发票
这些操作都是由用户在APP中点击“提交订单”按钮之后触发的。
那么首先来看APP这边。一般我们的APP仅仅负责UI层面的展示控制,业务逻辑部分都是下沉到后端的API去做的。而APP和API之间大多都是以Http或者Tcp协议的形式进行通信的,那么在APP层面,我们只要借助一些异步编程的类库即可(这方面不是特别专业,就不多BB了)。
然后到API层面,先给所有接收请求的Action加上异步支持,java的话可以在注解处增加asyncSupported = true,.net的话增加aysnc关键字。如此一来,就是告诉程序所在的宿主(web server或者service)“我这个方法是支持异步的,你接收到请求之后就不要阻塞了,去忙别的吧”。
接下来就轮到处理上面提到的电商下单场景中的4个操作了。理论上,这4个操作可以全部按「请求+回调」的异步模式进行,完全可行。这个过程其实有点像「并行」的意思,最终的处理完成时间是由最晚完成回调的那个操作决定的。
但是,为了避免个别程序的意外情况导致最晚回调的时间被拉的很长,我们就需要来考虑一下,那些无需即时知道甚至无需关心返回结果的操作可以通过「事件」的形式进行「异步」。
比如,像“生成电子发票”这种操作,对当前这个业务场景来说并不需要实时知道它的返回结果。
虽然我们知道它的业务逻辑相比生成订单这些更简单,处理起来很快,但是一旦服务出现问题,那就不好说了。
题外话:网络是不可信的,因为它容易受到攻击、不稳定,所以在分布式系统中这些“意外情况”格外常见。多一个硬性的依赖,就多一份出错的可能性。
如果没有做好前面一些文章中提到的「高可用」保障(文末放传送门,感兴趣的可以看完这篇再去看)的话,一旦所依赖的服务出现问题就会被拖累,导致接收到最晚回调的时间拉长,甚至由于未能及时回调回来导致当前的处理无法继续下去。
那像这样的业务点,我们就可以通过「事件」的形式进行「异步」处理,比如在生成完订单之后发出一个“订单被创建”的「事件」,然后由订阅该「事件」的“生成电子发票服务“接收该「事件」并进行处理。如此一来,即提高了“提交订单”时的处理效率,还使得“电子发票服务“的任何波动都不会影响到“提交订单”操作的正常进行。
对这个「事件」的处理,你可以在程序中建立一个单独的方法进行,它的入参是一个「事件」基类,返回值是void。具体的「事件」数据你可以选择持久化到DB,也可以选择投递到MQ中。大致是下面这样的代码
void SendEvent(BaseEvent event){ //投送到DB或者MQ; } class BaseEvent{ DateTime OccurredTime; } class OrderCreated extend BaseEvent{ Order order; Invoice invoice; ... }
可能你会问事件处理失败了怎么办?甚至做持久化和投递到MQ的s以后就异常了咋办?可以转去看之前的文章《分布式系统关注点——「共识」的兄弟「事务」》,以及文末的高可用系列文章。
最后,当你在使用异步的时候,还有一项工作要做,虽然是辅助性的,但是很重要。
就是需要引入一个全局唯一标识将整个异步的请求链路“串“起来,否则排查问题的时候够你头疼的,完全分不清楚哪是哪。如果条件允许,可以再引入一个日志聚合系统。比如ELK全家桶,让你可以更高效的筛选日志信息。
总结
好了,我们一起总结一下。
这次呢,Z哥先和你聊了下「异步」的意义,以及它是如何来提升性能的。
然后和你聊了一下「异步」的一些弊端和常见的运用方式。
最后以一个电商下单的例子梳理了一下做「异步」的思路。
希望对你有所启发。
相关文章:
作者:Zachary
出处:https://www.cnblogs.com/Zachary-Fan/p/async.html
如果你喜欢这篇文章,可以下方扫码关注我的个人「原创」公众号哦。
相关推荐
JBPM采购申请系统——12_JBPM异步(一).7z JBPM采购申请系统——12_JBPM异步(一).7z JBPM采购申请系统——12_JBPM异步(一).7z JBPM采购申请系统——12_JBPM异步(一).7z JBPM采购申请系统——12_JBPM异步(一).7z JBPM...
JBPM采购申请系统——13_JBPM异步(二).7z JBPM采购申请系统——13_JBPM异步(二).7z JBPM采购申请系统——13_JBPM异步(二).7z JBPM采购申请系统——13_JBPM异步(二).7z JBPM采购申请系统——13_JBPM异步(二).7z JBPM...
在这个领域,我们需要深入理解并掌握一系列关键知识点,以构建高效、可扩展且容错的系统。 1. **分布式系统基础**:分布式系统由多个独立的组件构成,它们之间通过网络进行通信。这些组件可以是服务器、数据库、...
在大型分布式系统中,消息队列可以作为不同组件间的通信媒介,确保数据的一致性和异步处理。 EJB是Java EE的一部分,提供了创建可部署、可扩展的企业级应用程序的标准方式。EJB容器管理了对象的生命周期,包括事务...
本资料“187深入浅出分布式技术原理”旨在帮助读者理解分布式系统的精髓,掌握如何设计和实现高效、可靠的分布式系统。 分布式技术的核心理念在于将任务分解到多个独立的节点上,以实现并行处理,提高系统的性能和...
### 分布式系统知识点梳理 #### 一、分布式系统概览 **1.1 分布式系统的目标** - **资源共享**:分布式系统的核心目标之一是资源共享,包括硬件资源(如处理器、存储器)、软件资源(应用程序和服务)、数据资源...
在《分布式系统原理与范型》第二版中,读者可以期待深入探讨这些范型的实现细节,以及如何在实际项目中应用。书中可能涵盖了诸如CAP定理、Paxos协议、Raft共识算法、Chubby锁服务、Zookeeper协调服务等经典理论和...
在这个关于分布式系统的PPT中,我们可以期待涵盖以下几个重要的知识点: 1. **分布式系统定义与特性**:首先,会介绍分布式系统的概念,包括其组成元素(如服务器、客户端、网络等)以及主要特征,如透明性、并发性...
这份"分布式系统设计PDF"资料对于任何希望深入理解这一领域的学习者,无论初级还是高级,都是一份极具价值的学习资源。 首先,分布式系统的概念及其重要性是学习的基础。分布式系统是由多个独立的计算机节点组成,...
本教程“深入浅出分布式技术原理”旨在帮助读者理解并掌握这一核心技术,通过学习,您可以深入了解分布式系统的设计、实现以及优化策略。 分布式技术的核心目标在于提高系统的可伸缩性、容错性和性能。它包括多个子...
分布式系统原理与泛型是计算机科学中的重要...通过学习分布式系统原理与泛型,我们可以设计出更高效、健壮和易于维护的分布式应用。这些课件将帮助你掌握关键概念,理解如何在实际项目中应用分布式系统设计和泛型技术。
总体来看,文章通过对基于ZooKeeper的分布式系统架构的设计与实现的描述,向读者展示了分布式系统架构设计的关键技术点,以及如何利用ZooKeeper这一工具来构建高可用的分布式服务模型。这不仅为分布式系统的开发人员...
"深入浅出分布式技术原理教程"旨在帮助读者理解和掌握分布式系统的基础知识和实践应用。本教程可能涵盖了以下关键知识点: 1. 分布式系统概念:分布式系统是由多台计算机通过网络连接,共同协作完成一个单一的任务...
1. **分布式系统定义与特性**:理解分布式系统的概念,包括其主要特征,如透明性(如位置透明、复制透明等)、并发性、异步通信以及资源的共享和协调。 2. **网络基础**:了解网络协议、IP地址、TCP/IP模型,以及...
这本书“分布式系统-原理与范例”可能是深入理解这一主题的关键资源,尽管这里提供的信息是英文版且清晰度不高。然而,对于熟悉英文的专业人士来说,这仍然是一个有价值的学习材料。 分布式系统的原理主要包括以下...
完整版"这本书中,作者深入探讨了构建高效、可靠且可扩展的分布式系统的关键技术和实践。 一、分布式系统基础 分布式系统的核心目标是透明性,即用户应能感知到系统是一个整体,而不会意识到其背后的分布式特性。这...
在IT行业中,Java语言因其平台无关性、丰富的库支持以及高效性能,在构建大型分布式系统方面扮演着重要角色。本文将深入探讨大型分布式系统中的Java...理解和掌握这些知识点,对于开发和维护高效的分布式系统至关重要。
- 异步分布式系统中可能会遇到各种故障,如遗漏、随机故障、时序故障,但不包括屏蔽故障。 - 内部和外部时钟同步用于提高时钟的准确性,内部同步可能就足够处理许多情况,因为系统只关心相对时间差。 7. **网络...
- **Distributed Systems for the Distributed Systems Engineer**:这本书深入浅出地介绍了分布式系统的各个方面,适合那些希望深入了解分布式系统设计原理和实践的工程师。 - **FLP Impossibility Result**:这...