Edited at

Azure ServiceFabric - Web Apiを実装してみる

More than 1 year has passed since last update.

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を作成する

image.png


構成

image.png

・Actorが定期的にDBからデータを取得し、Stareful Serviceのデータを更新

・Web APIにアクセスの際はStateful Serviceをコールし、Stateful Serviceで永続化されたデータを返す


実装


Service Fabricのソリューションを作成

ソリューションの新規作成からService Fabricを選択する

image.png

最初に作成するサービスのタイプを選択。(今回はStateful Serviceから作成するためステートフルサービスを選択)

image.png

上記でステートフルサービスを選択した場合、Service Fabric、ステートフルサービスの2つのプロジェクトが作成される

image.png


Sateful Serviceの実装


Interfaceプロジェクト作成

Interfaece用のプロジェクトを新規で追加する。

image.png

別のサービスとのデータをやり取りするためのModelを作成


User.cs

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を作成


IUpdateHogeService.cs

public interface IUpdateHogeService : IService

{
/// <summary>
/// Hoge ServiceのUserデータを更新
/// </summary>
/// <param name="users"></param>
/// <returns></returns>
Task Update(IEnumerable<User> users);
}

StatefulServiceに実装する(追記:2017/06/13データ更新がされないバグのため修正)


HogeStatefulService.cs

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を作成


IGetHogeService.cs

public interface IGetHogeService : IService

{
/// <summary>
/// ユーザリストを取得する
/// </summary>
/// <returns></returns>
Task<User> GetUser(int id);
}

StatefulServiceに実装する


HogeStatefulService.cs

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に外部からのリクエストリスナーを追加


HogeStatefulService.cs

protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()

{
return new List<ServiceReplicaListener>()
{
new ServiceReplicaListener(
(context) =>
this.CreateServiceRemotingListener(context))
};
}


Actorの実装


プロジェクトの追加

Service Fabricのプロジェクトのコンテキストメニューから追加⇒Service Fabricサービスの新規作成...を選択する

image.png

アクターサービスを選択

image.png

ActorとInterfaceのプロジェクトが追加される

image.png


データ更新処理

データ更新用のInterfaceを作成


IHogeActor.cs

public interface IHogeActor : IActor

{
/// <summary>
/// データ更新を行う
/// </summary>
/// <returns></returns>
Task Update();
}

Actorに実装


HogeActor.cs

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が終了されない場合があるので、終了処理を追記)


HogeActor.cs

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メソッドに実装する。


HogeActor.cs

public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period)

{
if (reminderName == "Hoge Alarm")
{
await Update();
}
}

(2017/06/17追記) Alarmの終了処理を記述(終了処理の記述がないと、更新のデプロイ時に失敗するので必ず終了処理を記述)


HogeActor.cs

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サービスの新規作成...を選択する

image.png

ステートレスWebApiを選択

image.png

WebApiのプロジェクトが追加される

image.png


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を作成)

image.png

構成マネージャーですべてx64にしVisual Studioの開始ボタンを押下すると、エミュレーターへの

デプロイが開始される

Service Fabric Explorerを確認し、各サービスでエラーがないことを確認

image.png

ブラウザやRest ClientからApiをたたいてみる。

image.png

APIの実行結果が確認できる。


その他

StateMangerのReliableDictionaryはレコードの容量が大きくなるとパフォーマンスが悪くなるので注意。

MSDNでは80kByte未満が推奨。

テスト周りではまりポイントが多いので、次はテストについて書きます。