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

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を工夫したらマルチテナントで遊べると思います。

 

おまけ。

広告

コメントを残す

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

WordPress.com ロゴ

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

Twitter 画像

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

Facebook の写真

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

Google+ フォト

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

%s と連携中