水一程--架构设计--Reactive Message Arc--Scala Akka
目的: 使用 Scala , Akka 构建 反应式 架构系统
reference:
本文未做特定书籍说明的页面,均指《响应式架构——消息模式Actor实现》
企业级软件 并发和并行设计 原则:
1. 如果因共享资源导致了问题(甚至导致网络分区),就应该停止共享资源,停止共享队列,停止共享领域模型实体并停止共享对它们执行操作的进程。
2.如果正式的运行环境会出现难以处理的突发情况,那么应尽可能地使我们的开发环境向其靠拢.
3.如果难以将线程设计的适合系统中所有需要处理的事项,应仅在有需要处理的任务时,为特定类型的任务分配专门处理这类任务的线程.
4.如果通过轮询方式分配工作的效率较低,就不要使用轮询方式分配工作。将系统中的线程设计为 在能够处理下一项工作时,才接收工作,只有当系统中的组件
做好处理工作的准备且能够处理这项工作的可用线程时,组件才应对分配给它的工作做出回应。而不应通过主动轮询的方式获取要做的工作。
5.如果难以调度任务或者调度任务容易引发任务,可将这项工作交给擅长这方面的 软件处理,应将注意力集中在自己应用程序的开发工作上。
6.软件出故障是不可避免的,应通过预先制定的应对方案设计系统,使系统具有容错性.
1. 通过Actor 对象传递消息
2.消息通道
3.消息结构
4.消息路由
5.消息转换
6.消息端点
7.系统管理和基础结构
规范化消息模型 p123
使用样本类可以高效地创建 规范化 消息模型中 的消息类型.
样本类:
1.可专门用于支持 Scala 语言的模式匹配功能.
2.是创建不可变值类型的最简单方式之一.
在 Scala 源代码模块中声明消息的类型时, 应遵守几个常见的惯例.
1. 应将在源代码中由指定类型的 Actor对象支持的所有消息,与接收者 Actor 对象的定义放在一起。当你编写的 Actor 系统主要使用本地 Actor 对象,且系统中所有的 Actor对象
都能够通过软件包级的操作访问任何全局类型数据时,这样做能够取得最佳效果。这种源代码级的关联能够使为哪种 Actor 对象发送哪种消息的细节变得清晰,这样指定的接收者
Actor 对象就能够理解它收到的消息了.
2. 将所有通用消息类型放置在一个 Scala 源文件中。 当整合多个Actor 系统中指定集合中的 Actor 对象,并统一定义规范化消息模型(一种在消息总线中推荐使用的处理方式)时,
这样做可以取得最佳效果。
当需要跨多个远程 Actor 系统使用这些类型的消息,且必须将这些消息类型部署在多个系统中,由这些系统中的接收者或发送者使用时,这样做尤为有用.
现在只需使用一个或少数几个源代码模块,就能够找到能够跨多个Actor系统的消息。
2021-05-30

样本类时创建不可变值对象 最简单方式,字段自动声明为 val 变量,自动生成 equals(), hashCode(), toString(), copy(..),
使用样本类创建在 Actor 对象之间传递的不可变消息。 样本类会创建完美的值对象。提供了一种执行模式匹配。
Acttor 对象收到消息时就会用到模式匹配功能。 Actor 对象分辨它所收到消息是哪种 类型的途径。
使用类型 和 结果参数。 使用样本类定义消息类型。
只要某个 Actor 对象存在,使用 ActorRef 或 ActorSelection 方法都可以向该对象发送消息.
但该 Actor 对象可能处于正在停止或已经停止的状态,此情况下 消息无法被送达.
为确保能够发送指定的消息,必须采用 重新加载和重试 策略,使用 Akka Persistence 完成此类任务。
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-persistence_2.13</artifactId> <version>2.6.14</version> </dependency>


向 user 守护对象所有的直接子对象 发送了 FlushAll 消息.
Actor 模型与 对象模型区分开的 一个因素是:
Actor 对象能够以动态方式更改本身的行为。
状态设计模式, Scala 和 Akka, 通过使用 ActorContext 对象。 Actor 对象能够通过
接受代码块从一种行为切换到另一种行为.
2021-06-01
Akka 集群
支持多节点的,容错的,分布式Actor系统。通过创建节点集群来做到这一点。使用 TCP 通过某个端口通信的ActorSystem 对象,只有这样才能拥有唯一标识.
Gossip 协议, 要求素有节点不间断报告它们的可用情况。以便集群能够维持正常运转.
主机名:端口号 : ActorySystem 实例提供的标识符
这三个部分组合到一起在集群的生命周期种具有唯一性.
集群的节点事件
1. CurrentClusterState 第一个事件 P74 6个事件
集群单例对象:中心节点 或者 工作路由器(所有工作任务必须经过一个路由器,该路由器就能够控制完成任务的方式并监控它们的完成程度)
Akka Persistence journal 插件配置集群分片。
集群分片只能确保将 Actor 对象均匀地分布到集群中的节点中,
如果某些分片或节点有较重的负载,分片无法解决,因为 共用数据 的性能和负载问题 位于应用程序层面。
默认情况下,消息是以至多执行一次的方式被发送的.
AtLeastOnceDelivery 是 Akka 的 Persistence 工具包提供的一种功能. 使用该功能可以确保能够成功将消息送达目的地.
Actor 对象扩展了 PersistentActor 类,并使用了 AtLeastOnceDelivery 特性,以便能够从节点故障和丢失消息情况中恢复.
集群客户端
ClusterReceptionist 管理客户端 Actor 对象 与 集群内部 Actor 对象之间的通信。
在集群内部使用该对象可以注册将会向集群外部的客户端提供服务的Actor 对象。其必须存在与集群中的每个节点中。
集群路由器
Akka框架中的路由器以两种形式存在:分组和池
分组形式:路由器会与一组已经存在的,在集群中的多个节点中正在运行的Actor对象通信。
池形式:路由器能够控制 Actor 对象的生命周期,能够在不同节点中创建新的Actor对象。
知道有多少个路由器为一个 Actor 对象分组提供路由服务时,分组形式会变得有用。
配置集群时,许多节点可以被划分为做相同工作(通常为后台服务)的 Actor对象分组。
许多前台处理程序都会拥有指向提供后台服务的Actor 对象分组的 集群路由器.
2021-06-02
池形式中,路由器负责管理它为之提供路由服务的Actor对象的生命周期,一对多的关系,整个 集群中仅存在一个为Actor对象提供路由服务的路由器,
将路由器定义为集群单例对象。
在 Actor 对象扮演完 资源密集型任务的 工人角色时,池形式最有用。
一致性工作是指每条消息会占用大致相等的资源。路由器是以平均方式向集群中的节点分配负载.
负载均衡路由器,处理 Actor 对象池 和分组。
heap: JVM 有多少可用内存。
load: UNIX 系统平均负载 (CPU 运行队列长度为基础)
cpu: 查明 CPU 使用情况
mix : 用于查明堆内存,CPU 和 负载的情况.
JMX 会收集这些信息,但是使用本地操作系统监视,不会获得太精确的结果。
要使用 Akka 集群中的 Hyperic Sigar 工具集, 自带的软件库。
<!-- https://mvnrepository.com/artifact/org.fusesource/sigar --> <dependency> <groupId>org.fusesource</groupId> <artifactId>sigar</artifactId> <version>1.6.4</version> </dependency>
将集群路由器 从一致性散列型 转换为 自适应负载均衡性
计算密集型,CPU 使用率一般都会接近 100%, 因此根据 CPU 运行队列长度 metrics-selector = load 观察负载情况可以取得更好效果。
可以查明有多少进程正在等待使用 CPU。
度量标准事件,负载均衡路由器使用的度量负载的信息可以被直接使用。要监听度量负载的事件,可以使Actor 对象订阅 ClusterMetricsChanged实例。
将负载监控器创建为集群单例对象会很有用。它能够通过消息实时了解负载情况,比查询 JMX 中的托管Bean 或使用 UNIX 命令行使用程序效果更好.
scala test
<!-- https://mvnrepository.com/artifact/org.scalatest/scalatest --> <dependency> <groupId>org.scalatest</groupId> <artifactId>scalatest_2.13</artifactId> <version>3.3.0-SNAP3</version> <scope>test</scope> </dependency>
行为型测试消息处理 p98
BDD 和 实例化需求 (Specification By Example , SBE) 是只一种将重点放在业务需求上的测试模式。目的是反映出真正的乐舞场景,并将这些场景用作软件的需求。
精雕细琢软件模型,通过基于需求的测试中的断言验证行为。
2021-06-02
多线程问题:
1.死锁;
2.活锁;
3.线程饥饿
4.低效代码
5.伪共享 , L1,L2,L3, 高速缓存 中高速缓存行,A,B 修改同行,导致 通过内存刷新数据的高速缓存行.
预测不出处理器调度任务的事件。多线程代码具有不确定性的原因。
多线程软件天生会导致线程之间存在临时耦合性和依赖性的原因。
临时耦合性的问题,通过临时方式降低, 使用队列管理由线程执行的各项工作,通常是推荐的处理方式:
一个线程向队列中添加新的任务时,处理工作的线程(工人线程)会从队列中去除任务。
所有工人线程都会从工作队列取出它们的下一个任务。能减少线程之间的联系。线程之间的耦合性就会更低.
可能在添加工作的线程 和 工人线程 之间导致 争用队列的情况。
工人线程还有可能获得 共享资源 (如模型实体) 重合的任务,从而导致 访问资源的操作被阻塞.
ConcurrentLinkedQueue 等无锁队列。但如果没有专门的防伪共享设计 ,也无法取得 理想的并发效果.
即使通过 分析单个 对象(如队列)可以防止在一组连续的存储空间中 出现伪共享情况,但通常难以查明一个
告诉缓存行中可能含有哪些独立对象。
2021-06-04
管道与过滤器
将许多处理步骤链接到一起的方式构成处理过程,每个处理步骤都不会与其他处理步骤耦合,因此当出现新需求时,
所有处理步骤会被重新安排或替换.
2021-06-05
使用弱架构,如JSON, 使用基于分析程序的运行时转换方式,还可通过 接触序列化操作,将指定消息直接转换为本地对象或记录类型
通用数据,字典,映射或函数式语言中的特定记录类型。
消息的类型, 由复杂性排列
命令消息 < 事件消息 < 文档消息 . p135
命令消息的输出很可能是事件消息。
事件消息, 消息的 类型/名称 本身就传达了许多事件的含义,通常会含有一个表明已发生情况的动词过去式。许多情况下,事件消息
仅被用作标识符和标量数据,接收系统转换事件消息时,会变成一条命令并被分派给本地应用程序服务。
文档消息非常适合传输具有复杂和精细结构的大型有效数据负荷。需要使用消息封装 比命令或事件更多的消息时,就应使用文档消息。
消息通道
1. 点对点通道:Actor 模型必不可少组成部分,替代了面向对象系统中常规的方法调用模式。Actor对象之间的通信自然而然地形成了一种 点对点通道,发送消息的Actor对象必须知道接收消息的Actor对象地址,展示出消息次序。
2.发布-订阅 : Akka 既支持本地,也支持远程 发布-订阅 功能
3. 数据类型通道:接收消息的App在不检查消息内容的情况下,接收指定数据类型的消息。
4.非法数据类型通道:Actor对象无法识别或在当前状态下无法处理的消息。
5.死信通道:当Actor系统无法将某条消息送达接收者,就会将这条消息发给死信通道。将消息发送给已经停止且不会再接收消息的对象时。
6.确保送达机制: Akka 框架

浙公网安备 33010602011771号