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

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

1 import akka.actor._
2 import akka.actor.SupervisorStrategy._
3 import scala.concurrent.duration._
4 import akka.util.Timeout
5 import akka.event.LoggingReceive
6 import akka.pattern.{ask, pipe}
7 import com.typesafe.config.ConfigFactory
8  
9 /**
10  * Runs the sample
11  */
12 object 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  */
37 class 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  
58 object 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  */
73 class 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  
107 object 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  */
126 class 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

你可能感兴趣的:(Scala,教程)