Service Fabricについては下記のMSDNのページを参照するとよい
Sevice Fabricの概要
開発環境を準備する
最初の Azure Service Fabric アプリケーションを作成する
ローカル クラスターでアプリケーションのデプロイおよびアップグレードを開始する
Azure ポータルを使用して Azure で Service Fabric クラスターを作成する
環境
- Visual Studio 2017
- Azure SDK 2.9.6
- Microsoft Azure Service Fabric SDK 2.6.204
- Service FabricのHost OSはWindows Server、言語はC#で作成。
*Host OSをLinuxにしたり、C#以外の言語でも開発自体は可能
仕様
API仕様
下記のテーブルを参照し、ユーザIDからをJson形式で名前を返すAPIを作成する
構成
・Actorが定期的にDBからデータを取得し、Stareful Serviceのデータを更新
・Web APIにアクセスの際はStateful Serviceをコールし、Stateful Serviceで永続化されたデータを返す
実装
Service Fabricのソリューションを作成
ソリューションの新規作成からService Fabricを選択する
最初に作成するサービスのタイプを選択。(今回はStateful Serviceから作成するためステートフルサービスを選択)
上記でステートフルサービスを選択した場合、Service Fabric、ステートフルサービスの2つのプロジェクトが作成される
Sateful Serviceの実装
Interfaceプロジェクト作成
別のサービスとのデータをやり取りするためのModelを作成
public class User
{
/// <summary>
/// ID
/// </summary>
public int Id { get; set; }
/// <summary>
/// FirstName
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// LastName
/// </summary>
public string LastName { get; set; }
}
データ更新処理
データ更新用のInterfaceを作成
public interface IUpdateHogeService : IService
{
/// <summary>
/// Hoge ServiceのUserデータを更新
/// </summary>
/// <param name="users"></param>
/// <returns></returns>
Task Update(IEnumerable<User> users);
}
StatefulServiceに実装する(追記:2017/06/13データ更新がされないバグのため修正)
private static readonly string _dictionaryName = "MyDictionary";
public async Task Update(IEnumerable<User> users)
{
var myDictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<int, User>>(_dictionaryName);
using (ITransaction transaction = this.StateManager.CreateTransaction())
{
foreach (User user in users)
{
await myDictionary.AddOrUpdateAsync(transaction, user.Id, user, (key, value) => user);
}
await transaction.CommitAsync();
}
}
データ取得処理
データ取得用のInterfaceを作成
public interface IGetHogeService : IService
{
/// <summary>
/// ユーザリストを取得する
/// </summary>
/// <returns></returns>
Task<User> GetUser(int id);
}
StatefulServiceに実装する
public async Task<User> GetUser(int id)
{
var myDictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<int, User>>(_dictionaryName);
using (ITransaction transaction = this.StateManager.CreateTransaction())
{
return (await myDictionary.TryGetValueAsync(transaction, id)).Value;
}
}
リスナーの実装
CreateServiceReplicaListenersに外部からのリクエストリスナーを追加
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new List<ServiceReplicaListener>()
{
new ServiceReplicaListener(
(context) =>
this.CreateServiceRemotingListener(context))
};
}
Actorの実装
プロジェクトの追加
Service Fabricのプロジェクトのコンテキストメニューから追加⇒Service Fabricサービスの新規作成...を選択する
データ更新処理
データ更新用のInterfaceを作成
public interface IHogeActor : IActor
{
/// <summary>
/// データ更新を行う
/// </summary>
/// <returns></returns>
Task Update();
}
Actorに実装
public async Task Update()
{
ServiceProxyFactory serviceProxyFactory = new ServiceProxyFactory();
IUpdateHogeService service =
serviceProxyFactory.CreateServiceProxy<IUpdateHogeService>(new Uri(@"fabric:/Hoge/HogeStatefulService"), new ServicePartitionKey(0));
using (HogeDbContext context = new HogeDbContext())
{
var data = await context.Users.Select(u => new HogeStatefulService.Interface.Models.User()
{
Id = u.Id,
FirstName = u.FirstName,
LastName = u.LastName
}).ToListAsync();
await service.Update(data);
}
}
※Azure ServiceのエミュレータからLocalDBに接続する場合は下記を参照
Azure Service Fabric - エミュレーターからLocalDBへの接続
Alarmの設定
先ほど作成したUpdateメソッドを定期的に実行して、DBからデータを取得するため、
Alarmを実装する。
定期的に処理を実行する機能としてAlarmの他にTimerがある。
AlarmとTimerの違いは下記を参照。
アクターのタイマーとアラーム
OnactivateAsyncメソッドにAlarmの起動処理を実装
RegisterReminderAsyncは引数の順番にAlarm名、Alarmに関連付けられた状態、実行遅延、実行間隔となる
(2017/08/30追記 原因不明だが、アップデート時に前回起動のreminderが終了されない場合があるので、終了処理を追記)
ActorEventSource.Current.ActorMessage(this, "Actor activated.");
try
{
var registerReminder = GetReminder(RequestLimitDataActorName);
await UnregisterReminderAsync(registerReminder);
}
catch (ReminderNotFoundException)
{
// 処理なし
}
var reminder = await this.RegisterReminderAsync(
"Hoge Alarm",
null,
TimeSpan.FromMilliseconds(0),
TimeSpan.FromMinutes(1)
);
IRemindable Interfaceを継承し、Alarmから呼ばれる処理をReceiveReminderAsyncメソッドに実装する。
public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period)
{
if (reminderName == "Hoge Alarm")
{
await Update();
}
}
(2017/06/17追記) Alarmの終了処理を記述(終了処理の記述がないと、更新のデプロイ時に失敗するので必ず終了処理を記述)
protected override async Task OnDeactivateAsync()
{
var register = GetReminder("Hoge Alarm");
await UnregisterReminderAsync(register);
await base.OnDeactivateAsync();
}
ActorのAlarmやタイマーの起動はOnActiveAsyncメソッドで起動する。
OnActiveAsyncはActor内のいずれかのメソッドがコールされたタイミングで実行されるため、
最低1回はメソッドをコールする必要がある。
下記を参考にActorServieを独自のものにすることで、起動時にメソッドをコールし、自動的に
AlarmやTimerを起動することができる。
Azure Service Fabric - ActorのTimerやAlermを自動で起動
Web APIの実装
プロジェクトの追加
Service Fabricのプロジェクトのコンテキストメニューから追加⇒Service Fabricサービスの新規作成...を選択する
Web Apiの実装
適当なControllerを作成し、Getメソッドを実装
public async Task<HttpResponseMessage> Get(int id)
{
ServiceProxyFactory serviceProxyFactory = new ServiceProxyFactory();
IGetHogeService service =
serviceProxyFactory.CreateServiceProxy<IGetHogeService>(new Uri(@"fabric:/Hoge/HogeStatefulService"), new ServicePartitionKey(0));
HogeStatefulService.Interface.Models.User result = await service.GetUser(id);
return this.Request.CreateResponse(
HttpStatusCode.OK,
new { Result = result });
}
エミュレーターで実行してみる
Visal Studioの構成マネージャーを開きすべてx64になっていることを確認。
AnyCpuの場合はx64に設定(存在しない場合はAnyCpuをベースにx64を作成)
構成マネージャーですべてx64にしVisual Studioの開始ボタンを押下すると、エミュレーターへの
デプロイが開始される
Service Fabric Explorerを確認し、各サービスでエラーがないことを確認
APIの実行結果が確認できる。
その他
StateMangerのReliableDictionaryはレコードの容量が大きくなるとパフォーマンスが悪くなるので注意。
MSDNでは80kByte未満が推奨。
テスト周りではまりポイントが多いので、次はテストについて書きます。