快速连续向机器人发送多条消息使其崩溃
Multiple messages to the bot in quick succession crash it
设置
我有一个在 .NET + Bot Framework + Azure + Facebook Messenger 上运行的机器人。
初始问题
我试图解决向机器人发送多条消息时触发异常和 HTTP 错误 412 的问题。Microsoft 在此处描述了此问题:https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict
第一个解决方案
在上面的页面中,Microsoft 提供了一个过时的示例代码来解决此问题。在 this github issue 中,该代码有一个应该可以工作的修订版本。我把它放在我的 MessageController 的构造函数中:
static MessagesController()
{
// Prevent exception in the bot and HTTP error 412 when the user
// sends multiple messages in quick succession. This may cause
// potential problems with consistency of getting/setting user
// properties.
// See https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict
// for details. The above link contains wrong code sample, revised
// code is from here: https://github.com/Microsoft/BotBuilder/issues/2345
var builder = new ContainerBuilder();
builder
.Register(c => new CachingBotDataStore(c.ResolveKeyed<IBotDataStore<BotData>>(typeof(ConnectorStore)), CachingBotDataStoreConsistencyPolicy.LastWriteWins))
.As<IBotDataStore<BotData>>()
.AsSelf()
.InstancePerLifetimeScope();
builder.Update(Conversation.Container);
}
第二题
现在,当我快速连续向机器人发送多条消息时,仍然出现异常。但是,它从 HTTP 错误 412 更改为其他内容:
One or more errors occurred. at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result() at MyBot.SetUserDataProperty(Activity activity, String PropertyName, String ValueToSet) in C:\Users\xxx.cs:line 230
Update: 我检查了上面的InnerException
,结果是同样的旧HTTP错误412:
The remote server returned an error: (412) Precondition Failed.
违规代码是一个写入机器人存储的函数。上面引用的第 230 行是该函数的最后一行:
public static void SetUserDataProperty(Activity activity, string PropertyName, string ValueToSet)
{
StateClient client = activity.GetStateClient();
BotData userData = client.BotState.GetUserData(activity.ChannelId, activity.From.Id);
userData.SetProperty<string>(PropertyName, ValueToSet);
//client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
// Await async call without making the function asynchronous:
var temp = Task.Run(() => client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result;
}
问题
我还能做些什么来确保用户在写入 BotState 存储时能够快速连续发送多条消息而不会触发异常?
我觉得这里有几个问题
您尝试执行此操作的方式 activity.GetStateClient();
仅用于原型制作。我们不建议将此方法用于生产级代码。您可以在对话框中设置用户数据,如 context.UserData.SetValue("food", "Nachos" );
,当对话框被序列化时,这些值将自动保存。
很可能您是从对话框中调用此方法 SetUserDataProperty
,因此当您这样做时 var temp = Task.Run(() => client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result;
它会发生冲突并导致错误。
请查看此 blog post 以了解更多信息
以下是如何实施您的跟进问题:
if (activity.Type == ActivityTypes.Message)
{
var message = activity as IMessageActivity;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = new AddressKey()
{
BotId = message.Recipient.Id,
ChannelId = message.ChannelId,
UserId = message.From.Id,
ConversationId = message.Conversation.Id,
ServiceUrl = message.ServiceUrl
};
ConversationReference r = new ConversationReference();
var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);
userData.SetProperty("key 1", "value1");
userData.SetProperty("key 2", "value2");
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
await botDataStore.FlushAsync(key, CancellationToken.None);
}
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
您将需要实施这个 class 或类似的东西:
public class AddressKey : IAddress
{
public string BotId { get; set; }
public string ChannelId { get; set; }
public string ConversationId { get; set; }
public string ServiceUrl { get; set; }
public string UserId { get; set; }
}
设置
我有一个在 .NET + Bot Framework + Azure + Facebook Messenger 上运行的机器人。
初始问题
我试图解决向机器人发送多条消息时触发异常和 HTTP 错误 412 的问题。Microsoft 在此处描述了此问题:https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict
第一个解决方案
在上面的页面中,Microsoft 提供了一个过时的示例代码来解决此问题。在 this github issue 中,该代码有一个应该可以工作的修订版本。我把它放在我的 MessageController 的构造函数中:
static MessagesController()
{
// Prevent exception in the bot and HTTP error 412 when the user
// sends multiple messages in quick succession. This may cause
// potential problems with consistency of getting/setting user
// properties.
// See https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict
// for details. The above link contains wrong code sample, revised
// code is from here: https://github.com/Microsoft/BotBuilder/issues/2345
var builder = new ContainerBuilder();
builder
.Register(c => new CachingBotDataStore(c.ResolveKeyed<IBotDataStore<BotData>>(typeof(ConnectorStore)), CachingBotDataStoreConsistencyPolicy.LastWriteWins))
.As<IBotDataStore<BotData>>()
.AsSelf()
.InstancePerLifetimeScope();
builder.Update(Conversation.Container);
}
第二题
现在,当我快速连续向机器人发送多条消息时,仍然出现异常。但是,它从 HTTP 错误 412 更改为其他内容:
One or more errors occurred. at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) at System.Threading.Tasks.Task1.get_Result() at MyBot.SetUserDataProperty(Activity activity, String PropertyName, String ValueToSet) in C:\Users\xxx.cs:line 230
Update: 我检查了上面的InnerException
,结果是同样的旧HTTP错误412:
The remote server returned an error: (412) Precondition Failed.
违规代码是一个写入机器人存储的函数。上面引用的第 230 行是该函数的最后一行:
public static void SetUserDataProperty(Activity activity, string PropertyName, string ValueToSet)
{
StateClient client = activity.GetStateClient();
BotData userData = client.BotState.GetUserData(activity.ChannelId, activity.From.Id);
userData.SetProperty<string>(PropertyName, ValueToSet);
//client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
// Await async call without making the function asynchronous:
var temp = Task.Run(() => client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result;
}
问题
我还能做些什么来确保用户在写入 BotState 存储时能够快速连续发送多条消息而不会触发异常?
我觉得这里有几个问题
您尝试执行此操作的方式 activity.GetStateClient();
仅用于原型制作。我们不建议将此方法用于生产级代码。您可以在对话框中设置用户数据,如 context.UserData.SetValue("food", "Nachos" );
,当对话框被序列化时,这些值将自动保存。
很可能您是从对话框中调用此方法 SetUserDataProperty
,因此当您这样做时 var temp = Task.Run(() => client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result;
它会发生冲突并导致错误。
请查看此 blog post 以了解更多信息
以下是如何实施您的跟进问题:
if (activity.Type == ActivityTypes.Message)
{
var message = activity as IMessageActivity;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = new AddressKey()
{
BotId = message.Recipient.Id,
ChannelId = message.ChannelId,
UserId = message.From.Id,
ConversationId = message.Conversation.Id,
ServiceUrl = message.ServiceUrl
};
ConversationReference r = new ConversationReference();
var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);
userData.SetProperty("key 1", "value1");
userData.SetProperty("key 2", "value2");
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
await botDataStore.FlushAsync(key, CancellationToken.None);
}
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
您将需要实施这个 class 或类似的东西:
public class AddressKey : IAddress
{
public string BotId { get; set; }
public string ChannelId { get; set; }
public string ConversationId { get; set; }
public string ServiceUrl { get; set; }
public string UserId { get; set; }
}