jptiancai / Actor

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

<响应式架构 -- 消息模式Actor实现与Scala,Akka应用集成> 笔记:


什么是Actor模型?

Actor模型使Actor对象能够像第一类值一样被处理,并通过消息传递功能加强了Actor对象之间的通信操作.因为消息是以异步方式发送的,所以对Actor对象执行的操作也是高度并发化的,这自然会使应用程序能够以并发方式解决问题.通常,每个Actor对象都负责单个的应用程序任务. 因为Actor是一种无锁的并发代理对象,而且执行它们的线程通常不会被阻塞,所以使用Actor对象设计的应用程序能够完美地利用系统的基础线程资源.

Actor模型是一种用于处理并发计算的数学模型,它将Actor对象用做并发计算的通用单元.与其他计算模型不同,发明Actor模型的灵感源于物理学理论,如广义相对论和量子力学[Actor,Model]

可以将Actor模型视为创建响应式应用程序的手段之一.使用这种具体的响应式软件开发方法,可以细致地处理响应式应用程序的主要方面:响应性,韧性,弹性和消息驱动性.

Actor系统和Actor对象具有下列基本特点:

  • 直接通过异步消息传递方式进行通信(ZMQ传输)
  • 状态机:通过变为另一种消息处理器,Actor对象就成了一种有限状态机
  • 无共享:一个Actor对象不会与其他Actor对象或相关组件共享可变状态
  • 无锁的并发处理方式:因为Actor对象不会共享它们的可变状态,而且在同一时刻仅会接收一条消息,所以在对消息回应前,Actor对象永远都不需要尝试锁定它们的状态
  • 并行性
  • Actor对象的系统性: Carl Hewitt博士说,单独存在的Actor对象不是Actor对象.在Actor系统中,Actor对象才能成为Actor对象.

当一条消息经过�网络从一个Actor对象发送给另一个Actor对象时,消息本身一定会被序列化.该操作本身会增加系统开销,根据你选择的序列化类型,该系统开销可能会相当大.默认情况下,Akka框架使用Java序列化,这种序列化操作在性能和尺寸方面都比较差.因此,当你知道消息必须跨越多台JVM时,应因地制宜地选择适当的序列化操作.Protocol Buffers和Kryo是比较著名的序列化操作.可以配置你喜欢的序列化类型,并禁用Java序列化操作.


多线程开发中可能�出现的问题:

  • 死锁
  • 活锁
  • 饥饿
  • 低效代码
  • 伪共享

我们想要的是更简单的编写多线程软件的方式.如果临时耦合性是一个问题,那么我们就应该通过临时方式降低耦合性.使用队列管理由线程执行的各项工作,通常是推荐的处理方式.一个线程向队列中添加新的任务时,处理工作的线程(工人线程)会从队列中取出任务.因为所有工人线程都会从工作队列取出它们的下一个任务,所以这能够减少线程之间的联系.这样线程之间的耦合性就会更低了.

该处理方式中还有一个潜在的问题:� 这在需要使用资源的线程之间,生成了一种固有的�竞争关系.如果某个线程能够更快速地获得任务,它就会总是先于其他工人线程锁住共享的队列,从而导致其他工人线程处于饥饿状态.甚至为工作队列添加工作的线程也会受到该线程的影响,而经常无法获得为该队列添加工作的机会.而且,也会出现多个任务使用同一个系统实体的情况,从而导致多个线程之间出现的重叠的资源依赖性,进而导致出现死锁和活锁情况.即使能够避免死锁和活锁,被共享的资源本身也会导致争用资源的线程被阻塞,进而导致访问操作无法达最佳标准.

可以使用Java的ConcurrentLinkedQueue等无锁队列.然而,即使使用无锁队列,如果该队列没有专门的防伪共享设计,也无法取得最理想的并发效果.显然,即使使用Java和C#de标准线程机制,需要解决的问题也不仅仅是两三个.令人遗憾的是,编译器和多线程工具都不能帮助我们消除伪共享导致的隐形系统开销.即使通过分析单个对象(如队列)�可以防止在一组连续的存储空间中出现方为共享情况,但通常难以查明一个高速缓存中可能含有哪些独立对象.

怎样�才能将每个线程都设计得适合处理系统中任何类型的�任务呢?当然是有可能做到的,但做到这一点是否很简单�呢?此外,当前所有工人线程都是用基于轮询的队列访问操作,检索它们的下一项工作.我们是否可以改进这种模式,在了解到�工人线程能够执行下一个任务时,将工作分配给它们?

尽管工作队列通常更适用于多线程设计,而且取得成功的可能性也高得多,但它仍旧不是理想的模式.

摆在我们面前的问题是,怎样能够强制多个线程共享系统资源.单个的工作队列是一个较大的共享瓶颈.另一个潜在的共享瓶颈(死锁/��活锁灾区)是通过共享方式使用模型实体.

通常,真正需要我们关心的, �是当出现问题时怎样处理问题.当系统中正在运行的多个并发进程收到它们无法处理的数据, 以不正确的方式执行操作或彻底崩溃时,你应该怎样处理呢?

正是由于并发和并行处理方式有这些潜在的副作用,所以主流的商务企业级解决方案�大多避免使用这类设计.这也是�在大多数��情况中多线程设计仅被前沿项目使用的原因,前沿项目必须压榨出每一分计算潜力并通过CPU�核心和网络节点扩展规模.


Actor模型的作用

Actor系统可以帮助你同时利用多个处理器核心.为了使指定的Actor对象能够对它通过并发方方式收到的消息作出回应,每个处理器核心都会被充分利用.调度器通常被用于将线程池中的编程分配给缓存了消息的Actor对象.在繁忙的系统中消息会不断进入许多Actor对象的消息�缓存中,调度器会不停地为线程分配工作.一直以无阻塞方式使用线程,可以获得理想的,高效的并发处理系统.

因此Actor 模型并不是要在单个调用操作层级上取得突破.正如Gul Agha所提出的那样,顺序程序不擅长支持并发和并行处理方式的原因是,它们在处理并发情况是容易出现死锁.这也是我们使用Actor模型的�最大原因.

1.�不共享:Actor �模型���专门规定Actor对象�之间不共享任何可变状态. 2.随机应变:......

About