Akka 编程(20):容错处理(一)

Akka】Actor模型探索 Akka是什么Akka就是为了改变编写高容错性和强可扩展性的并发程序而生的。通过使用Actor模型我们提升了抽象级别,为构建正确的可扩展并发应用提供了个更好的平台。在容错性方面我们采取了“let it crash”(让它崩溃)模型,人们已经将这种模型用在了电信行业,构建出“自愈合”的应用和永不停机的系统,取得了巨大成功。Actor还为透明的分布式系统以及真正的可扩展高容错应用的基础进行了抽象。Ak 阅读详情

我们在前面介绍Actor系统时说过每个Actor都是其子Actor的管理员,并且每个Actor定义了发生错误时的管理策略,策略一旦定义好,之后不能修改,就像是Actor系统不可分割的一部分。
实用错误处理
首先我们来看一个例子来显示一种处理数据存储错误的情况,这是现实中一个应用可能出现的典型错误。当然实际的应用可能针对数据源不存在时有不同的处理,这里我们使用重新连接的处理方法。
下面是例子的源码,比较长,需要仔细阅读,最好是实际运行,参考日志来理解:

1import akka.actor._
2import akka.actor.SupervisorStrategy._
3import scala.concurrent.duration._
4import akka.util.Timeout
5import akka.event.LoggingReceive
6import akka.pattern.{ask, pipe}
7import com.typesafe.config.ConfigFactory
8 
9/**
10 * Runs the sample
11 */
12object FaultHandlingDocSample extends App {
13 
14  import Worker._
15 
16  val config = ConfigFactory.parseString( """
17      akka.loglevel = "DEBUG"
18      akka.actor.debug {
19      receive = on
20      lifecycle = on
21      }
22      """)
23 
24  val system = ActorSystem("FaultToleranceSample", config)
25  val worker = system.actorOf(Props[Worker], name = "worker")
26  val listener = system.actorOf(Props[Listener], name = "listener")
27  // start the work and listen on progress
28  // note that the listener is used as sender of the tell,
29  // i.e. it will receive replies from the worker
30  worker.tell(Start, sender = listener)
31}
32 
33/**
34 * Listens on progress from the worker and shuts down the system when enough
35 * work has been done.
36 */
37class Listener extends Actor with ActorLogging {
38 
39  import Worker._
40 
41  // If we don’t get any progress within 15 seconds then the service is unavailable
42  context.setReceiveTimeout(15 seconds)
43 
44  def receive = {
45    case Progress(percent) =>
46      log.info("Current progress: {} %", percent)
47      if (percent >= 100.0) {
48        log.info("That’s all, shutting down")
49        context.system.shutdown()
50      }
51    case ReceiveTimeout =>
52      // No progress within 15 seconds, ServiceUnavailable
53      log.error("Shutting down due to unavailable service")
54      context.system.shutdown()
55  }
56}
57 
58object Worker {
59 
60  case object Start
61 
62  case object Do
63 
64  final case class Progress(percent: Double)
65 
66}
67 
68/**
69 * Worker performs some work when it receives the ‘Start‘ message.
70 * It will continuously notify the sender of the ‘Start‘ message
71 * of current ‘‘Progress‘‘. The ‘Worker‘ supervise the ‘CounterService‘.
72 */
73class Worker extends Actor with ActorLogging {
74 
75  import Worker._
76  import CounterService._
77 
78  implicit val askTimeout = Timeout(5 seconds)
79  // Stop the CounterService child if it throws ServiceUnavailable
80  override val supervisorStrategy = OneForOneStrategy() {
81    case _: CounterService.ServiceUnavailable => Stop
82  }
83  // The sender of the initial Start message will continuously be notified
84  // about progress
85  var progressListener: Option[ActorRef] = None
86  val counterService = context.actorOf(Props[CounterService], name ="counter")
87  val totalCount = 51
88 
89  import context.dispatcher
90 
91  // Use this Actors’ Dispatcher as ExecutionContext
92  def receive = LoggingReceive {
93    case Start if progressListener.isEmpty =>
94      progressListener = Some(sender())
95      context.system.scheduler.schedule(Duration.Zero, 1 second, self, Do)
96    case Do =>
97      counterService ! Increment(1)
98      counterService ! Increment(1)
99      counterService ! Increment(1)
100      // Send current progress to the initial sender
101      counterService ? GetCurrentCount map {
102        case CurrentCount(_, count) => Progress(100.0 * count / totalCount)
103      } pipeTo progressListener.get
104  }
105}
106 
107object CounterService {
108 
109  final case class Increment(n: Int)
110 
111  case object GetCurrentCount
112 
113  final case class CurrentCount(key: String, count: Long)
114 
115  class ServiceUnavailable(msg: String) extends RuntimeException(msg)
116 
117  private case object Reconnect
118 
119}
120 
121/**
122 * Adds the value received in ‘Increment‘ message to a persistent
123 * counter. Replies with ‘CurrentCount‘ when it is asked for ‘CurrentCount‘.
124 * ‘CounterService‘ supervise ‘Storage‘ and ‘Counter‘.
125 */
126class CounterService extends Actor {
127 
128  import CounterService._
129  import Counter._
130  import Storage._
131 
132  // Restart the storage child when StorageException is thrown.
133  // After 3 restarts within 5 seconds it will be stopped.
134  override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 3,
135    withinTimeRange = 5 seconds) {
136    case _: Storage.StorageException => Restart
137  }
138  val key = self.path.name
139  var storage: Option[ActorRef] = None
140  var counter: Option[ActorRef] = None
141  var backlog = IndexedSeq.empty[(ActorRef, Any)]
142  val MaxBacklog = 10000
143 
144  import context.dispatcher
145 
146  // Use this Actors’ Dispatcher as ExecutionContext
147  override def preStart() {
148    initStorage()
149  }
150 
151  /**
152   * The child storage is restarted in case of failure, but after 3 restarts,
153   * and still failing it will be stopped. Better to back-off than continuously
154   * failing. When it has been stopped we will schedule a Reconnect after a delay.
155   * Watch the child so we receive Terminated message when it has been terminated.
156   */
157  def initStorage() {
158    storage = Some(context.watch(context.actorOf(Props[Storage], name ="storage")))
159    // Tell the counter, if any, to use the new storage
160    counter foreach {
161      _ ! UseStorage(storage)
162    }
163    // We need the initial value to be able to operate
164    storage.get ! Get(key)
165  }
166 
167  def receive = LoggingReceive {
168    case Entry(k, v) if == key && counter == None =>
169      // Reply from Storage of the initial value, now we can create the Counter
170      val = context.actorOf(Props(classOf[Counter], key, v))
171      counter = Some(c)
172      // Tell the counter to use current storage
173      c ! UseStorage(storage)
174      // and send the buffered backlog to the counter
175      for ((replyTo, msg) <- backlog) c.tell(msg, sender = replyTo)
176      backlog = IndexedSeq.empty
177    case msg@Increment(n) => forwardOrPlaceInBacklog(msg)
178 
179    case msg@GetCurrentCount => forwardOrPlaceInBacklog(msg)
180    case Terminated(actorRef) if Some(actorRef) == storage =>
181      // After 3 restarts the storage child is stopped.
182      // We receive Terminated because we watch the child, see initStorage.
183      storage = None
184      // Tell the counter that there is no storage for the moment
185      counter foreach {
186        _ ! UseStorage(None)
187      }
188      // Try to re-establish storage after while
189      context.system.scheduler.scheduleOnce(10 seconds, self, Reconnect)
190    case Reconnect =>
191      // Re-establish storage after the scheduled delay
192      initStorage()
193  }
194 
195  def forwardOrPlaceInBacklog(msg: Any) {
196    // We need the initial value from storage before we can start delegate to
197    // the counter. Before that we place the messages in a backlog, to be sent
198    // to the counter when it is initialized.
199    counter match {
200      case Some(c) => c forward msg
201      case None =>
202        if (backlog.size >= MaxBacklog)
203          throw new ServiceUnavailable(
204            "CounterService not available, lack of initial value")
205        backlog :+= (sender() -> msg)
206    }
207  }
208}
209 
210object Counter {
211 
212  final case class UseStorage(storage: Option[ActorRef])
213 
214}
215 
216/**
217 * The in memory count variable that will send current
218 * value to the ‘Storage‘, if there is any storage
219 * available at the moment.
220 */
221class Counter(key: String, initialValue: Long) extends Actor {
222 
223  import Counter._
224  import CounterService._
225  import Storage._
226 
227  var count = initialValue
228  var storage: Option[ActorRef] = None
229 
230  def receive = LoggingReceive {
231    case UseStorage(s) =>
232      storage = s
233      storeCount()
234    case Increment(n) =>
235      count += n
236      storeCount()
237    case GetCurrentCount =>
238      sender() ! CurrentCount(key, count)
239  }
240 
241  def storeCount() {
242    // Delegate dangerous work, to protect our valuable state.
243    // We can continue without storage.
244    storage foreach {
245      _ ! Store(Entry(key, count))
246    }
247  }
248}
249 
250object DummyDB {
251 
252  import Storage.StorageException
253 
254  private var db = Map[String, Long]()
255 
256  @throws(classOf[StorageException])
257  def save(key: String, value: Long): Unit = synchronized {
258    if (11 <= value && value <= 14)
259      throw new StorageException("Simulated store failure " + value)
260    db += (key -> value)
261  }
262 
263  @throws(classOf[StorageException])
264  def load(key: String): Option[Long] = synchronized {
265    db.get(key)
266  }
267}
268 
269object Storage {
270 
271  final case class Store(entry: Entry)
272 
273  final case class Get(key: String)
274 
275  final case class Entry(key: String, value: Long)
276 
277  class StorageException(msg: String) extends RuntimeException(msg)
278 
279}
280 
281/**
282 * Saves key/value pairs to persistent storage when receiving ‘Store‘ message.
283 * Replies with current value when receiving ‘Get‘ message.
284 * Will throw StorageException if the underlying data store is out of order.
285 */
286class Storage extends Actor {
287 
288  import Storage._
289 
290  val db = DummyDB
291 
292  def receive = LoggingReceive {
293    case Store(Entry(key, count)) => db.save(key, count)
294    case Get(key) => sender() ! Entry(key, db.load(key).getOrElse(0L))
295  }
296}

这个例子定义了五个Actor,分别是Worker, Listener, CounterService ,Counter 和 Storage,下图给出了系统正常运行时的流程(无错误发生的情况):
20140830001

 

其中Worker是CounterService的父Actor(管理员),CounterService是Counter和Storage的父Actor(管理员)图中浅红色,白色代表引用,其中Worker引用了Listener,Listener也引用了Worker,它们之间不存在父子关系,同样Counter也引用了Storage,但Counter不是Storage的管理员。

正常流程如下:

步骤描述
1progress Listener 通知Worker开始工作.
2Worker通过定时发送Do消息给自己来完成工作
3,4,5Worker接受到Do消息时,通知其子Actor CounterService 三次递增计数器,

CounterService 将Increment消息转发给Counter,它将递增计数器变量然后把当前值发送给Storeage保存

6,7 Workier询问CounterService 当前计数器的值,然后通过管道把结果传给Listener

下图给出系统出错的情况,例子中Worker和CounterService作为管理员分别定义了两个管理策略,Worker在收到CounterService 的ServiceUnaviable上终止CounterService的运行,而CounterService在收到StorageException时重启Storage。

20140830002

 

出错时的流程

步骤描述
1 Storage抛出StorageException异常
2 Storage的管理员CounterService根据策略在接受到StorageException异常后重启Storage
3,4,5,6 Storage继续出错并重启
7 如果在5秒钟之内Storage出错三次并重启,其管理员(CounterService)就终止Storage运行
8 CounterService 同时监听Storage的Terminated消息,它在Storeage终止后接受到Terminated消息
9,10,11 并且通知Counter 暂时没有Storage
12 CounterService 延时一段时间给自己发生Reconnect消息
13,14 当它收到Reconnect消息时,重新创建一个Storage
15,16 然后通知Counter使用新的Storage

这里给出运行的一个日志供参考。

Flink容错机制第五篇 Akka基本概念 篇谈到Flink的checkpoint通信的消息驱动用到了Akka,这篇就简介Akka的actor模型,并尽可能复习些以往的框架和多线程知识。 ,基础概念 了解Akka是什么之前,要知道些我们常见且常用的基础概念。 1. 并发与并行(Concurrency & Parallelism) 并发和并行概念类似但有不同,并发指的是两个或多个任务能同进行下去,但不定会在同... 阅读详情

相关推荐

Scala第二十章节(Akka并发编程框架、Akka入门案例、Akka定时任务代码实现、两个进程间通信的案例以及简易版spark通信框架案例)

1. 理解Akka并发编程框架简介 2. 掌握Akka入门案例 3. 掌握Akka定时任务代码实现 4. 掌握两个进程间通信的案例 5. 掌握简易版spark通信框架案例

m0_56525833的博客 2493

Akka用来编写分布式容错并发事件驱动应用程序的工具和运行时

Akka:用来编写分布式容错并发事件驱动应用程序的工具和运行时

AkkaAkka容错处理

当我们创建 Actor 时,新建的 Actor 都是作为另个 Actor 的子 Actor,父 Actor 负责监督子 Actor。监督的核心思想就是把对于失败的响应和可能引起失败的组件分隔开,并且把可能发生错误的组件通过层级结构来组织,以便管理。如果策略在监督者 Actor(而不是单独的类)中声明,则其决策者可以线程安全方式访问 Actor 的所有内部状态,包括获取对当前失败的子级的引用,可用作失败消息的getSender()。如果异常直升级到根守护者,它将以与上面定义的默认策略相同的方式处理它。

九师兄 890

让并发和容错更容易:Akka示例教程

Akka用Scala语言写成,为开发高并发、分布式和容错式应用提供了便利,对开发者隐藏了很大程度的复杂性。把Akka用好肯定需要了解比这个教程更多的内容,但是希望这里的介绍和示例能够引起你的注意并继续了解Akka。写并发程序很难。程序员不得不处理线程、锁和竞态条件等等,这个过程很容易出错,而且会导致程序代码难以阅读、测试和维护。所以,很多人不倾向于使用多线程编程。取而代之的是,他们使用单线程进程(译者注:只含有个线程的进程),依赖外部服务(如数据库、队列等)处理所需的并发或异步操作。虽然这种方法在有些情况下是可行的,但还有很多其他情况不能奏效。很多实时系统——例如交易或银行业务应用,或实时游

Akka 接收消息超时的处理_Receive Timeout

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33757609的博客 1230

4、Akka容错处理

监督(Supervision) 容错(fault tolerance)概念与 Actor 相关,Actor 模型中容错处理使用叫做监督(supervision)处理。监督的核心思想就是把对于失败的响应和可能引起失败的组件分隔开,并且把可能发生错误的组件通过层级结构来组织,以便管理。 在分布式系统中每个组件都是个定时炸弹,那么我们希望能够确保无论其中任何个发生爆炸,都不会引发链式反应,导致其他组件也爆炸。也可以说,我们希望能够隔离错误,或是将可能引发失败情况的组件分离开来。 监督的层级结构 Akka

数据工匠记 2533

Akka定时任务schedule()方法

Akka定时任务schedule()方法,什么是Akka定时任务schedule()方法?如何在actor外部获取Scheduler对象,为什么需要提供个隐式的ExecutionContext对象,用于执行定时任务?如何在actor内部获取Scheduler对象,schedule()方法的格式,Akka定时任务schedule()方法有哪些类型的延迟?固定延迟,固定频率,Duration类

Maverick_曲流觞的博客 1225

scala_Akka并发编程框架

文章目录Akka并发编程框架简介Akka介绍Akka特性Akka通信过程创建ActorAPI介绍入门案例实现步骤1. 创建Maven模块2. 创建并加载Actor3. 发送/接收消息Akka定时任务使用方式示例示例二实现两个进程之间的通信案例介绍1. Worker实现2. Master实现简易版spark通信框架案例案例介绍实现思路1. 工程搭建2. 构建Master和Worker3. Work...

Imflash的博客 637

Akka Terminated

package aia.faulttolerance import akka.actor._ import akka.actor.Terminated object DbStrategy2 { class DbWatcher(dbWriter: ActorRef) extends Actor with ActorLogging { context.w...

ainuanwei5320的博客 313

Akka 编程 20 容错处理

Akka 编程 20 容错处理

qq_43685118的博客 426

Akka 指南 之「容错

正如在「Actor System」中所解释的,每个 Actor 都是其子级的监督者,因此每个 Actor 定义了故障处理的监督策略。这策略不能在 Actor 系统启动之后改变,因为它是 Actor 系统结构的个组成部分。

安正勋的博客 3170

akka介绍

akka简介    开始想接触到akka,是在看些并发相关资料的时候,查了下akka的官方介绍,介绍如下:Akka个开发库和运行环境,可以用于构建高并发、分布式、可容错、事件驱动的基于JVM的应用,使构建高并发的分布式应用更加容易。 听到高并发和分布式这两个关键字就已经足够让人想去探索究竟是什么样的框架,当深入查看各种资料后,发现当前大数据领域火热的spark、flink底层的分布式计...

码农的世界你不懂 5万+

Akka框架——第节:并发编程简介

本节主要内容: 1. 重要概念 2. Actor模型 3. Akka架构简介多核处理器的出现使并发编程(Concurrent Programming)成为开发人员必备的项技能,许多现代编程语言都致力于解决并发编程问题。并发编程虽然能够提高程序的性能,但传统并发编程的共享内存通信机制对开发人员的编程技能要求很高,需要开发人员通过自身的专业编程技能去避免死锁、互斥等待及竞争条件(Race Con

摇摆少年梦的技术博客 2万+

Akka使用入门

Akka简单介绍 二Akka简单使用 从创建个scala项目说起 第Akka应用 a 定义个Actor b 客户端调用向actor发送消息 c Actor的生命周期 dActor编程模型的层次结构 akka容错机制 akka的远程调用 客户端应用入口 服务端入口 pojo类 客户端配置文件 服务端配置文件 三Spark20为什么放弃AkkaAkka适用场景 Akka简单介绍

dinghuiit的博客 2490

Akka编程讲解之构建高并发与可扩展系统

在当今的分布式系统中,高并发性、可扩展性和容错性是至关重要的特性。Akka作为个基于Actor模型的工具包,提供了种简洁而强大的方式来构建这些特性。通过Akka,开发者能够创建高效的并发应用,处理数百万个并发用户请求,且无需担心系统的稳定性。本文将深入探讨Akka的核心概念,并提供详细的代码示例,帮助您掌握如何利用Akka构建健壮的系统。Akka基于Actor模型,Actor是计算的基本单位,负责处理消息和管理状态。

hello.reader 1532

Java中的响应式编程Akka

Akka款基于Actor模型的工具包和运行时库,专为构建高并发、分布式和容错应用而设计。通过Actor来封装状态和行为,简化并发编程。通过透明的分布式消息传递机制,轻松扩展应用。通过Supervisor策略实现故障隔离和恢复。与Scala和Java语言紧密集成。响应式编程为我们提供了处理复杂系统交互的强大工具。Akka框架通过Actor模型,实现了高可扩展性、弹性和容错性,使得构建高并发、分布式应用变得更加容易。

weixin_53840353的博客 1246

Akka框架深度解析:从Actor模型到大数据的响应式架构

Actor模型是处理并发计算的数学模型,它将Actor作为通用的并发原语。每个Actor是个独立的计算单元,拥有自己的状态和邮箱(mailbox),并通过异步消息与其他Actor通信。fill:#333;important;important;fill:none;color:#333;color:#333;important;fill:none;fill:#333;height:1em;Actor系统发送消息更新状态发送消息创建子ActorActor AActor B的邮箱。

✨ 欢迎来到【Seal ^_^ 的CSDN博客】!✨ 2354
上一篇: Play Framework Web开发教程(19): 任务–启动一些进程
下一篇: 一个使用sbt编译的JNI C++ 的模板
引路蜂
博客等级 码龄15年 3122粉丝 711原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值