实现地图条目监听器

时间:2016-01-08 12:19:38

标签: scala akka listener

我正在尝试使用Scala实现Map条目监听器。

想法:

  1. 我需要订阅服务中的地图。
  2. 当添加/更新具有特定密钥的条目时,我需要通知所有订户。
  3. 我需要从其他服务访问地图以检查条目值。
  4. 我无法为此找到解决方案,所以我尝试用Akka实现它:

    class TrackingService(system: ActorSystem) extends LazyLogging {
      private val trackingActor = system.actorOf(TrackingActor.props)
      private val duration = Duration(15, TimeUnit.SECONDS)
      private implicit val timeout = Timeout(duration)
    
      def fireEvent(key: String): Unit = {
        TrackingActor ! EventFired(key)
      }
    
      def eventHappened(key: String): Future[Boolean] = {
        TrackingActor ? DoesEventHappened(key)).mapTo[Boolean]
      }
    
      def registerHiddenCardListener(key: String, data: MyData): Unit = {
        TrackingActor ! Subscribe(key, data)
      }
    }
    
    case class EventFired(key: String)
    case class EventHappened(key: String)
    case class EventHappenedResponse(happened: Boolean)
    
    case class Subscribe(key: String, data: Data)
    class TrackingActor extends Actor with LazyLogging {
      var eventMap: Map[String, Boolean] = Map.empty[String, Boolean]
      var dataMap: Map[String, List[Data]] = Map.empty[String, List[Data]]
    
      def receive: Receive = {
        case Subscribe(key, data)       =>
          val currentData: List[Data] = dataMap.getOrElse(key, Nil)
          val newData = data :: currentData
          dataMap = dataMap + (key -> newData)
        case EventHappened(key)         => sender() ! EventHappenedResponse(eventMap.getOrElse(key, false))
        case e@EventFired(key)          =>
          eventMap = eventMap + (key -> true)
    
          for {
            dataOpt <- dataMap.get(key)
            data <- dataOpt
          } {
            // do callback with data (e.g. send email)
          }
        case x => logger.warn(s"Received unknown message: $x")
      }
    }
    
    object TrackingActor {
      def props: Props = Props(classOf[TrackingActor])
    }
    

    我不喜欢这个解决方案:我不喜欢问模式,但我需要访问非演员类的条目。另外,我不喜欢有2张地图,但我需要存储一些应该用于回调的数据。

    关于如何改进这一点的任何想法?

1 个答案:

答案 0 :(得分:1)

这是一个想法:

function [fitresult, gof] = createFit_MSSI_Venus(RockerArm, mos)
%CREATEFIT1(ROCKERARM,MOS)
%  Create a fit.
%
%  Data for 'Psychometric curve fitting MSSI-RockeArm 3D mesh' fit:
%      X Input : RockerArm
%      Y Output: mos
%  Output:
%      fitresult : a fit object representing the fit.
%      gof : structure with goodness-of fit info.
%
%  See also FIT, CFIT, SFIT.

%  Auto-generated by MATLAB on 08-Jan-2016 15:40:41


%% Fit: 'Psychometric curve fitting MSSI-RockeArm 3D mesh'.
[xData, yData] = prepareCurveData( RockerArm, mos );

% Set up fittype and options.
ft = fittype( 'erfc((a+b*x)/sqrt(2))/2', 'independent', 'x',  
'dependent', 'y' );

opts = fitoptions( 'Method', 'NonlinearLeastSquares' );
opts.Algorithm = 'Levenberg-Marquardt';
opts.Display = 'Off';
opts.StartPoint = [0.655477890177557 0.171186687811562];

% Fit model to data.
[fitresult, gof] = fit( xData, yData, ft, opts );

% Plot fit with data.
figure( 'Name', 'Psychometric curve fitting MSSI-RockeArm 3D mesh' );
h = plot( fitresult, xData, yData);
set(h, 'Markersize',20);
legend( h, 'mos vs. RockerArm', 'Psychometric curve fitting MSSI-        
RockeArm 3D mesh', 'Location', 'NorthEast' );

% Label axes
xlabel MSSI-RockerArm 3D mesh
ylabel mos
grid on

通过执行此操作,您可以使订阅者保持非case class Subscribe[A, B](f: (A, B, NotifyingMap[A,B]) => Any) case class Event[A, B](key: A, value: B, originator: NotifyingMap[A,B]) case class RegisterObserver(actorRef: ActorRef) /** * Subscribes to events */ class Subscriber[A,B]{ def register(actorSystem: ActorSystem) = { val actor = actorSystem.actorOf(Props(classOf[Observer[A,B]])) actor ! Subscribe(handleEvent) } def handleEvent(key: A, value: B, notifyingMap: NotifyingMap[A, B]) = { println(s"Saw key $key with value $value") } } /** * Observer of events that will call a partial function when * an event comes in. */ class Observer[A, B] extends Actor{ var f: (A,B,NotifyingMap[A,B]) => Any = _ def receive = { case x: Subscribe[A, B] => f = x.f Notifier() ! RegisterObserver(self) case e: Event[A,B] => f(e.key, e.value, e.originator) } } /** * Notifier that sends out the event to all registered observers. */ class Notifier extends Actor { var observers = List[ActorRef]() def receive = { case x: RegisterObserver => observers = x.actorRef :: observers case x: Event[_,_] => observers.foreach(_ ! Event) } } /** * Singleton notifier. */ object Notifier{ var notifier: ActorRef = _ def create(actorSystem: ActorSystem) = actorSystem.actorOf(Props(classOf[Notifier])) def apply(): ActorRef = notifier } /** * Class that sends out an event when an item is put. Also allows for * getting an item based on a key. */ class NotifyingMap[A, B](){ val map: TrieMap[A,B] = TrieMap[A,B]() // Specific business logic here on when you publish the event. def put(key: A, value: B) = { map.put(key, value).foreach(v => Notifier() ! Event(key, v, this)) } def get(key: A) = map.get(key) } 类,同时仍允许其对事件做出反应。您也可以在Actor上调用简单的旧方法,因为它只是一个类,而不是NotifyingMap

我个人喜欢在邮件中存储回调信息。通常,您通过在案例类中使用另一个Actor来看到这一点。在此示例中,我们在案例类中有ActorRef,因此我们知道事件的起源,并可以在那里适当地调用NotifyingMap方法。

完全披露:我没有运行任何此代码。它确实编译。