動的にモジュールを読み込む

Windows Azure Blog Storage上にアセンブリを置いて動的に読み込んでみましょう。

何の変哲もないインターフェース用意して

image

適当に2つほど実装して個別のアセンブリを作っておきます。

imageimage

MarshalByRefObjectがポイントですね。

ビルドしたアセンブリはBlobにUpしておきましょう。

一応Privateコンテナです。

さて今回はサンプルなのでWorkerRole内でアセンブリをロードして定期的に実行するようにします。

で、実際にアセンブリをロードするわけです。

		private void LoadAssembly(string AssemblyUrl,string AssemblyTypeName)
		{
			if (SampleAppDomain != null)
			{
				AppDomain.Unload(SampleAppDomain);
			}

			PermissionSet trustedLoadFromRemoteSourceGrantSet
				= new PermissionSet(PermissionState.Unrestricted);

			AppDomainSetup trustedLoadFromRemoteSourcesSetup = new AppDomainSetup();
				trustedLoadFromRemoteSourcesSetup.ApplicationBase =
					AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

			SampleAppDomain = AppDomain.CreateDomain("SampleDomain", null, trustedLoadFromRemoteSourcesSetup, trustedLoadFromRemoteSourceGrantSet);
			var webpermission = new WebPermission(
						NetworkAccess.Connect,
						AssemblyUrl
					);
			SampleAppDomain.PermissionSet.SetPermission(webpermission);
			SampleAppDomain.PermissionSet.Demand();
			SampleAssembly = (ISample.ISample)SampleAppDomain.CreateInstanceFromAndUnwrap(AssemblyUrl, AssemblyTypeName);
		}

 

こんな感じですね。リモートホスト上にアセンブリがあるのでWebPermissionで権限を付けてます。それ以外はいたって普通。

あとBlobをプライベートコンテナに置いているので、共有アクセス署名(SAS: Shared Access Signature)を付けないとアクセスできません。

		private string GetAssemblyUrl()
		{
			var assemblyName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobName");
			var assemblyContainerName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobContainerName"); ;
			var connectionString = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobStorage"); ;

			var account = CloudStorageAccount.Parse(connectionString);
			var client = account.CreateCloudBlobClient();

			var assemblyBlobContainer = client.GetContainerReference(assemblyContainerName);

			var policy = new SharedAccessPolicy
			{
				Permissions = SharedAccessPermissions.Read,
				SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),
				SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(10)
			};

			CloudBlob blob = assemblyBlobContainer.GetBlobReference(assemblyName);
			var accessurl = blob.Uri.ToString() + blob.GetSharedAccessSignature(policy);
			Trace.WriteLine(accessurl);

			return accessurl;
		}

 

SAS付けた状態のURLを返してもらって、そいつをCreateInstanceFromAndUnwrapに渡します。

ポイントはアクセス期限の開始時刻を現在時刻からマイナス5分してるところぐらいですかね。。。Blob Storage上の時刻よりローカルPCのほうが進んでたために、アクセスした時刻が署名で許可してる期間外になってFileNotFoundExceptionになるというヒドイ目を見ました。ふぅ。

最後にロードしたいアセンブリの情報をサービス設定ファイルに書いておきましょう。

image

で、サービス設定ファイルが更新されたらアセンブリをロードしなおすようにします。

		public override bool OnStart()
		{
			LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName"));

			RoleEnvironment.Changed += (s, e) =>
			{
				if (e.Changes.Any(chg => chg is RoleEnvironmentConfigurationSettingChange))
				{
					LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName"));
				}
			};

			return base.OnStart();
		}

とりあえずこんな感じ。

じゃ実行してみましょう。

起動した時はSample1.dllがロードされて、サービス設定ファイルをSample2.dllに変更してUpdateしたらちゃんとロードしなおされてますね!

あとは気が向いたらBlob上のDLLを入れ替えるなりしてロードしなおせばいいわけです。

もちろんロードした後はローカルのキャッシュで動作してるので、SASの期限切れても問題ないですしBlob上からファイルがなくなっても問題ないです。

拡張モジュールの実装とかいろいろ出来そうですね。AppDomainを工夫したらマルチテナントで遊べると思います。

 

おまけ。

using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
namespace WorkerRole1
{
public class WorkerRole : RoleEntryPoint
{
private ISample.ISample SampleAssembly;
private AppDomain SampleAppDomain;
public override void Run()
{
while (true)
{
Trace.WriteLine(SampleAssembly.Execute());
Thread.Sleep(5000);
}
}
public override void OnStop()
{
if (SampleAppDomain != null)
{
AppDomain.Unload(SampleAppDomain);
}
base.OnStop();
}
public override bool OnStart()
{
LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName"));
RoleEnvironment.Changed += (s, e) =>
{
if (e.Changes.Any(chg => chg is RoleEnvironmentConfigurationSettingChange))
{
LoadAssembly(GetAssemblyUrl(), RoleEnvironment.GetConfigurationSettingValue("AssemblyTypeName"));
}
};
return base.OnStart();
}
private void LoadAssembly(string AssemblyUrl,string AssemblyTypeName)
{
if (SampleAppDomain != null)
{
AppDomain.Unload(SampleAppDomain);
}
PermissionSet trustedLoadFromRemoteSourceGrantSet
= new PermissionSet(PermissionState.Unrestricted);
AppDomainSetup trustedLoadFromRemoteSourcesSetup = new AppDomainSetup();
trustedLoadFromRemoteSourcesSetup.ApplicationBase =
AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
SampleAppDomain = AppDomain.CreateDomain("SampleDomain", null, trustedLoadFromRemoteSourcesSetup, trustedLoadFromRemoteSourceGrantSet);
var webpermission = new WebPermission(
NetworkAccess.Connect,
AssemblyUrl
);
SampleAppDomain.PermissionSet.SetPermission(webpermission);
SampleAppDomain.PermissionSet.Demand();
SampleAssembly = (ISample.ISample)SampleAppDomain.CreateInstanceFromAndUnwrap(AssemblyUrl, AssemblyTypeName);
}
private string GetAssemblyUrl()
{
var assemblyName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobName");
var assemblyContainerName = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobContainerName"); ;
var connectionString = RoleEnvironment.GetConfigurationSettingValue("AssemblyBlobStorage"); ;
var account = CloudStorageAccount.Parse(connectionString);
var client = account.CreateCloudBlobClient();
var assemblyBlobContainer = client.GetContainerReference(assemblyContainerName);
var policy = new SharedAccessPolicy
{
Permissions = SharedAccessPermissions.Read,
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(5),
SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromMinutes(10)
};
CloudBlob blob = assemblyBlobContainer.GetBlobReference(assemblyName);
var accessurl = blob.Uri.ToString() + blob.GetSharedAccessSignature(policy);
Trace.WriteLine(accessurl);
return accessurl;
}
}
}

view raw
gistfile1.cs
hosted with ❤ by GitHub

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中