バッチ処理のためのメッセージキューとしてMicrosoftのAzure Storage Queueを使っている。C#での使い勝手は言わずもがなだし、Rubyでも最低限のSDKが提供されているのでAWSと比べて使い勝手が悪いということはない。
ただ、Rubyでメッセージを追加してC#でメッセージを取り出すということをやっていたらエンコード関係ではまったのでその部分について今日はメモしておきたい。
例外が発生するケース
Rubyでメッセージ追加
基本的には公式のドキュメントを参考にした。
キュー サービスを使用する方法 (Ruby) | Microsoft Azure
メッセージを追加はRailsアプリケーションから行いたかったので、上のドキュメントを参考にしてこんな風にかいた。
1# config/initializer/azure.rb
2Azure.configure do |config|
3 config.storage_account_name = ENV['AZURE_STORAGE_ACCOUNT']
4 config.storage_access_key = ENV['AZURE_STORAGE_ACCESS_KEY']
5end
6
7AZURE_QUEUE_SERVICE = Azure::QueueService.new
8AZURE_QUEUE = ENV['AZURE_QUEUE']
9AZURE_QUEUE_SERVICE.create_queue(AZURE_QUEUE)
1# in controller
2AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s)
C#でメッセージ取り出し
同じく公式ドキュメントを参考にして、メッセージを取り出す部分を書いた。
1private void Func()
2{
3 var sc = new StorageCredentials( Constants.AZURE_STOREGE_ACCOUNT, Constants.AZURE_STOREGE_ACCSESS_KEY );
4 var storageAccount = new CloudStorageAccount( sc, true );
5 var queueClient = storageAccount.CreateCloudQueueClient();
6 var queue = queueClient.GetQueueReference( Constants.AZURE_QUEUE );
7 queue.CreateIfNotExists();
8 var timeSpan = new System.TimeSpan(1, 0, 0);
9 var message = queue.GetMessage( timeSpan );
10 messsage.AsString;
11}
例外発生
実装はめちゃくちゃ簡単だしこれで動くだろうと思ったのだが、message.AsStringの部分でBase-64 文字配列または文字列の長さが無効です。という例外が発生してしまった。しかもAzure Storage Explorerなどのツールでも同じ例外が起こった。
SDKのソースを見てみたところ、メッセージにはエンコードした文字列かそうでないものかのタイプを持っているらしいが、どうやら今回のケースでは部エンコードされていない文字列であるにもかかわらず、タイプがエンコード済となっていて、想定外のコードを通ることになってしまっていたらしい。
azure-storage-net/CloudQueueMessage.Common.cs at master · Azure/azure-storage-net
1/// <summary>
2/// Gets the content of the message, as a string.
3/// </summary>
4/// <value>A string containing the message content.</value>
5public string AsString
6{
7 get
8 {
9 if (this.MessageType == QueueMessageType.RawString)
10 {
11 return this.RawString;
12 }
13 else
14 {
15 byte[] messageData = Convert.FromBase64String(this.RawString);
16 return utf8Encoder.GetString(messageData, 0, messageData.Length);
17 }
18 }
19}
回避策
これもソースをみたらわかったんだけど、RubyのSDKでエンキューするときにエンコードオプションを指定できるみたいだった。
1def create_message(queue_name, message_text, options={})
2 query = { }
3 unless options.empty?
4 query["visibilitytimeout"] = options[:visibility_timeout] if options[:visibility_timeout]
5 query["messagettl"] = options[:message_ttl] if options[:message_ttl]
6 query["timeout"] = options[:timeout].to_s if options[:timeout]
7 end
8 uri = messages_uri(queue_name, query)
9 body = Serialization.message_to_xml(message_text, options[:encode])
10 call(:post, uri, body, {})
11 nil
12end
options[:encode]の部分!
そういうわけで、最初の実装にオプションを追加してこうしてあげればよかった。
1# in controller
2AZURE_QUEUE_SERVICE.create_message(AZURE_QUEUE, @model.id.to_s, encode: true)
例外もでなくなりました。めでたし!