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