提问人:Malik Tanzeel 提问时间:9/26/2022 最后编辑:Malik Tanzeel 更新时间:9/27/2022 访问量:26
TimerCallback 函数在部署的环境 C 中未命中#
TimerCallback function not getting hit in deployed environment C#
问:
我有一个Microsoft聊天机器人C#代码,它有一个TimerCallback函数,该函数在用户一段时间后运行。在本地运行代码时,此函数会命中,但在 Azure 环境中部署时,同一函数不会命中。
对于代码中过多的日志记录行,这只是为了验证函数是否在部署的环境中被击中。
以下是完整的代码:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Lebara.Crm.Bot.Core.Data;
using Lebara.Crm.Bot.Core.ServiceContracts;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.Extensions.Logging;
namespace Lebara.Crm.Bot.Services
{
public class ChatSessionTimeoutMiddleware : ActivityHandler, IMiddleware
{
private readonly IDistributedCache _distributedCache;
private readonly string _cachekey;
private readonly BotOptions _botOptions;
private readonly ISystemMessageSender _systemMessageSender;
private readonly CustomConversationStateAccessors _customConversationStateAccessors;
private readonly ITelemetryManager _telemetryManager;
private readonly ISessionHandler _sessionHandler;
private readonly IServiceProvider _serviceProvider;
private static ConcurrentDictionary<string, Timer> _sessionTimers = new ConcurrentDictionary<string, Timer>();
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
private readonly ConcurrentDictionary<string, ConversationReference> _conversationReferences;
private string ObjectKey;
private readonly TelemetryClient telemetry = new TelemetryClient();
private readonly ILogger<ChatSessionTimeoutMiddleware> _logger;
// Timer related variables, made global, to avoid collected by Garbage Collector as they were created in memory previously.
private Timer _warningTimer = null;
private Timer _warningTimerGCIssue = null;
private Timer _timer = null;
private Timer _timerGCIssue = null;
public ChatSessionTimeoutMiddleware(
IDistributedCache distributedCache,
IConfiguration configuration,
IOptions<BotOptions> options,
ISystemMessageSender systemMessageSender,
CustomConversationStateAccessors customConversationStateAccessors,
ITelemetryManager telemetryManager,
ISessionHandler sessionHandler,
IServiceProvider serviceProvider,
IBotFrameworkHttpAdapter adapter,
ConcurrentDictionary<string, ConversationReference> conversationReferences,
ILogger<ChatSessionTimeoutMiddleware> logger)
{
_cachekey = $"{configuration["RedisCachingRoot"]}session-timeout:";
_distributedCache = distributedCache;
_botOptions = options.Value;
_systemMessageSender = systemMessageSender;
_customConversationStateAccessors = customConversationStateAccessors;
_telemetryManager = telemetryManager;
_sessionHandler = sessionHandler;
_serviceProvider = serviceProvider;
_adapter = adapter;
_conversationReferences = conversationReferences;
_appId = configuration["MicrosoftAppId"] ?? string.Empty;
_logger = logger;
}
private void AddConversationReference(Activity activity)
{
var conversationReference = activity.GetConversationReference();
_conversationReferences.AddOrUpdate(conversationReference.User.Id, conversationReference, (key, newValue) => conversationReference);
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate nextTurn, CancellationToken cancellationToken)
{
_logger.LogDebug("chat session timeout middleware");
//telemetry.TrackEvent("ChatSessionMiddleware - OnTurnAsync");
//telemetry.TrackTrace("ChatSessionMiddleware - OnTurnAsync", SeverityLevel.Warning, null);
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (turnContext.Activity.Type == ActivityTypes.Message
&& !string.IsNullOrEmpty(turnContext.Activity.Text)
&& !hasChatSessionRunning)
{
_logger.LogDebug("chatsessiontimeout OnTurnAsync if statement");
var key = _cachekey + turnContext.Activity.Conversation.Id;
_logger.LogDebug($"Key {key}");
var warningKey = "warning_" + key;
_logger.LogDebug($"WarningKey {warningKey}");
var period = _botOptions.InactivityPeriod;
_logger.LogDebug($"chatsessiontimeout period {period}");
var warningPeriod = _botOptions.WarningInactivityPeriod;
_logger.LogDebug($"chatsessiontimeout warningPeriod {warningPeriod}");
var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = period.Add(period)
};
_logger.LogDebug($"cacheOptions {cacheOptions}");
AddConversationReference(turnContext.Activity as Activity);
await _distributedCache.SetStringAsync(key, JsonConvert.SerializeObject(DateTime.Now.Add(period)), cacheOptions);
await _distributedCache.SetStringAsync(warningKey, JsonConvert.SerializeObject(DateTime.Now.Add(warningPeriod)), cacheOptions);
var timerPeriod = period.Add(TimeSpan.FromSeconds(1));
var warningTimePeriod = warningPeriod.Add(TimeSpan.FromSeconds(1));
_warningTimer = null;
_warningTimerGCIssue = null;
_sessionTimers.TryGetValue(warningKey, out _warningTimer);
_logger.LogDebug($"warningTimer {_warningTimer}");
if (_warningTimer == null)
{
_logger.LogDebug("warningTimer is null");
_warningTimerGCIssue = new Timer(new TimerCallback(WarningCallback), (warningKey, turnContext), warningTimePeriod, warningTimePeriod);
_warningTimer = _sessionTimers.GetOrAdd(warningKey, _warningTimerGCIssue);
}
_timer = null;
_timerGCIssue = null;
_sessionTimers.TryGetValue(key, out _timer);
_logger.LogDebug($"timer {_timer}");
if (_timer == null)
{
_logger.LogDebug("timer is null");
_logger.LogDebug($"key {key}");
_timerGCIssue = new Timer(new TimerCallback(Callback), (key, turnContext), timerPeriod, timerPeriod);
_timer = _sessionTimers.GetOrAdd(key, _timerGCIssue);
}
_warningTimer.Change(warningTimePeriod, warningTimePeriod);
_logger.LogDebug("chatSessionTimeoutMiddleware timer change");
_timer.Change(timerPeriod, timerPeriod);
}
_logger.LogDebug("chatSessionTimeoutMiddleware nextturn");
await nextTurn(cancellationToken).ConfigureAwait(false);
}
private async void Callback(object target)
{
//telemetry.TrackEvent("ChatSessionMiddleware - InactivityCallback");
_logger.LogDebug("ChatSessionMiddleware InactivityCallback");
var tuple = ((string, ITurnContext))target;
ObjectKey = tuple.Item1;
var turnContext = tuple.Item2;
foreach (var conversationReference in _conversationReferences.Values)
{
_logger.LogDebug("ChatSessionMiddleware InactivityCallback for loop");
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, EndOfChatCallback, default(CancellationToken));
}
}
private async void WarningCallback(object target)
{
//telemetry.TrackEvent("ChatSessionMiddleware - WarningCallback");
_logger.LogDebug("ChatSessionMiddleware WarningCallback");
var tuple = ((string, ITurnContext))target;
ObjectKey = tuple.Item1;
var turnContext = tuple.Item2;
foreach (var conversationReference in _conversationReferences.Values)
{
_logger.LogDebug("ChatSessionMiddleware WarningCallback for loop");
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId, conversationReference, WarningMessageCallback, default(CancellationToken));
}
}
private async Task WarningMessageCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
//telemetry.TrackEvent("ChatSessionMiddleware - WarningMessageCallback");
_logger.LogDebug("ChatSessionMiddleware WarningMessageCallback");
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
void DisposeTimer()
{
bool found = _sessionTimers.TryRemove(ObjectKey, out var timer);
if (found)
{
timer.Dispose();
timer = null;
}
}
var json = await _distributedCache.GetStringAsync(ObjectKey);
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (hasChatSessionRunning)
{
DisposeTimer();
return;
}
if (!string.IsNullOrEmpty(json))
{
var sessionEnd = JsonConvert.DeserializeObject<DateTime>(json);
if (DateTime.Now >= sessionEnd)
{
//telemetry.TrackEvent("ChatSessionMiddleware - SendingWarningMessage");
_logger.LogDebug("ChatSessionMiddleware SendingWarningMessage");
await _systemMessageSender.SendSystemMessage(turnContext, customConversationState, turnContext.Activity, ResourceIds.BotWarningEndOfChat);
}
}
DisposeTimer();
}
private async Task EndOfChatCallback(ITurnContext turnContext, CancellationToken cancellationToken)
{
//telemetry.TrackEvent("ChatSessionMiddleware - EndOfChatCallback");
_logger.LogDebug("ChatSessionMiddleware EndOfChatCallback");
var chatSdk = (IChatProvider)_serviceProvider.GetService(typeof(IChatProvider));
var customConversationState = await _customConversationStateAccessors.CustomConversationState.GetAsync(turnContext, () => new CustomConversationState());
void DisposeTimer()
{
bool found = _sessionTimers.TryRemove(ObjectKey, out var timer);
if (found)
{
timer.Dispose();
timer = null;
}
}
var json = await _distributedCache.GetStringAsync(ObjectKey);
var hasChatSessionRunning = await _sessionHandler.HasRunningSessionAsync(turnContext.Activity.Conversation.Id);
if (hasChatSessionRunning)
{
DisposeTimer();
return;
}
if (!string.IsNullOrEmpty(json))
{
var sessionEnd = JsonConvert.DeserializeObject<DateTime>(json);
if (DateTime.Now >= sessionEnd)
{
var parts = ObjectKey.Split(new char[] { ':' });
var dict = new Dictionary<string, string>
{
{"EndTime", json },
{"State", JsonConvert.SerializeObject(customConversationState) }
};
_telemetryManager.TrackEvent("AutomaticChatClosing", parts[parts.Length - 1], dict);
DisposeTimer();
//telemetry.TrackEvent("ChatSessionMiddleware - SendingEndOfChatMessage");
_logger.LogDebug("ChatSessionMiddleware SendingEndOfChatMessage");
await _systemMessageSender.SendSystemMessage(turnContext, customConversationState, turnContext.Activity, ResourceIds.BotAutomaticEndOfChat);
await Task.Delay(2000);
await chatSdk.EndChat(customConversationState.ChatContext, turnContext);
}
}
else
{
DisposeTimer();
}
}
}
}
代码产生问题/或未命中:
if (_warningTimer == null)
{
_logger.LogDebug("warningTimer is null");
_warningTimerGCIssue = new Timer(new TimerCallback(WarningCallback), (warningKey, turnContext), warningTimePeriod, warningTimePeriod);
_warningTimer = _sessionTimers.GetOrAdd(warningKey, _warningTimerGCIssue);
}
上述部分应在特定警告时间后调用 WarningCallback 函数,它在本地运行代码时会成功调用它,但不会在部署的环境中调用它。
答: 暂无答案
评论