LoginSignup
6
6

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-05-25

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未満が推奨。
テスト周りではまりポイントが多いので、次はテストについて書きます。

6
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6