VB.NETでもリアクティブしたい(Akka.NET)
はじめに
2018年は殺伐としたVB.NET界隈にAkka.NETの波が到来します。(しません)
という訳であまり需要がなさげなエントリですが*1、ライブラリ自体は結構成熟しているっぽいので実運用に突っ込んでもまぁ行けるんじゃないかなと思います。
Akkaとは
Akkaの概念自体については割と記事があるのでここでは割愛します。
インストールパッケージ
今回の記事で作成したプロジェクトでは以下のパッケージをNuGetでインストールします。
- Akka(Akka本体)
- Akka.Logger.Serilog(Serilogのログアダプタ)
- Serilog(ロガー)
- Serilog.Sinks.Console(Serilogのコンソールシンクの実装)
今回はロガーとしてSerilogを使用しますが、Serilog自体の説明は省略します。
メッセージとActorの実装
Akka.NETではActorはReceiveActor
(もしくはUntypedActor
)を継承して実装します。
ReceiveActor
ではコンストラクタ中で処理するメッセージの型とメッセージを処理するデリゲートを指定します。
Public Sub New(dest As IActorRef) _dest = dest Receive(Of String)(AddressOf ReceiveString) Receive(Of Greet)(AddressOf ReceiveGreet) End Sub
また、メッセージは(Akkaに限らず)スレッド間を行き来するためイミュータブルになるように実装します。*2
本家のScalaではcase class
で所望のクラスが簡単に量産できますが、VBではイミュータブルなクラスの定義がクッソ面倒*3なので弊社のプロダクト環境ではメタプログラミングとT4を駆使してコード生成をして対応しています。が本題ではないのでここでは触れません。
Public Class HelloActor Inherits ReceiveActor Private ReadOnly _logger As ILoggingAdapter = Context.GetLogger(New SerilogLogMessageFormatter()) Private ReadOnly _dest As IActorRef Public Sub New(dest As IActorRef) _dest = dest Receive(Of String)(AddressOf ReceiveString) Receive(Of Greet)(AddressOf ReceiveGreet) End Sub Public Sub ReceiveString(msg As String) _dest.Tell(New Greet(msg)) End Sub Public Sub ReceiveGreet(msg As Greet) _logger.Info("Returned {Message}", msg.Message) End Sub End Class Public Class EchoActor Inherits ReceiveActor Private ReadOnly _logger As ILoggingAdapter = Context.GetLogger(New SerilogLogMessageFormatter()) Public Sub New() Receive(Of Greet)(AddressOf ReceiveGreet) End Sub Public Sub ReceiveGreet(msg As Greet) _logger.Info("Receive {Message}", msg.Message) Sender.Tell(New Greet($"You said {msg.Message}")) End Sub End Class Public Class Greet Public ReadOnly Property Message As String Public Sub New(msg As String) Message = msg End Sub End Class
余談ですが、なんで
Receive(Of Greet)(AddressOf ReceiveGreet)
みたいな書き方をしているかなのですが、C#では
Receive<string>(message => { log.Info("Received String message: {0}", message); Sender.Tell(message); });
Receive(Of Greet)(Sub(message) _logger.Info("Returned {Message}", message.Message) End Sub)
みたいになってお゛ぉ゛ぉ゛ぉ゛ぉ゛ぉ゛ぉ゛んってなるからです。 特に行数が10行を超えた時点でクッソ読みづらくなります。
ActorSystem
あとは本家と同じようにActorSystem
を生成しーのActor
を生成しーのトツギーノでリアクティブできます。
Module Program Dim conf As String = " akka { loggers=[""Akka.Logger.Serilog.SerilogLogger, Akka.Logger.Serilog""] stdout-loglevel = off loglevel = DEBUG log-config-on-start = on actor { debug { receive = on autoreceive = on lifecycle = on event-stream = on unhandled = on } } } " Sub Main() Dim logger = New LoggerConfiguration(). WriteTo.Console(). MinimumLevel.Debug(). CreateLogger() Log.Logger = logger Dim system = ActorSystem.Create("vb-actor", conf) Dim echo = system.ActorOf(Of EchoActor)("echo") Dim hello = system.ActorOf(Props.Create(Function() New HelloActor(echo))) hello.Tell("hello") hello.Tell("hoge") Console.ReadLine() system.Terminate().Wait() End Sub End Module
[23:09:14 INF] Receive hello [23:09:14 INF] Receive hoge [23:09:14 INF] Returned You said hello [23:09:14 INF] Returned You said hoge
おわりに
Akka.NETはいかんせん日本語記事がほとんど存在しないので割と茨の道ですが、英語の記事自体は結構あるうえに本家のドキュメントも脳内で.NET向けにトランスパイルすれば割とそのまま適用できるので思ったよりは障壁は低いと思います。
なお、IntelliSenseの説明文がときたま「TBD」になっており、『はぁそうですか』ってなります。
おわり