337 lines
11 KiB
C#
337 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
using Nettention.Proud;
|
|
using RabbitMQ.Client.Events;
|
|
using RabbitMQ.Client;
|
|
using Google.Protobuf;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using System.Reflection;
|
|
using Newtonsoft.Json;
|
|
using NLog;
|
|
using MongoDB.Bson.IO;
|
|
|
|
|
|
|
|
using ServerCore;
|
|
|
|
|
|
|
|
namespace ServerBase;
|
|
|
|
public abstract partial class RabbitMqConnector : RabbitMQConnectorBase, IRabbitMqSession, IModule, IWithPacketNamespaceVerifier
|
|
{
|
|
public delegate Task FnServerMessageRecvFromConsumer(BasicDeliverEventArgs ea, IMessage message);
|
|
public FnServerMessageRecvFromConsumer? _fnServerMessageRecvFromConsumer;
|
|
|
|
private readonly PacketReceiver m_packet_receiver;
|
|
|
|
private ModuleContext m_module_context;
|
|
|
|
public RabbitMqConnector( ModuleContext moduleContext )
|
|
: base( getConfigParamsOrThrow(moduleContext).serviceName
|
|
, getConfigParamsOrThrow(moduleContext).address
|
|
, getConfigParamsOrThrow(moduleContext).port
|
|
, getConfigParamsOrThrow(moduleContext).userName
|
|
, getConfigParamsOrThrow(moduleContext).password
|
|
, getConfigParamsOrThrow(moduleContext).useSSL )
|
|
|
|
{
|
|
m_module_context = moduleContext;
|
|
m_packet_receiver = new PacketReceiver(this);
|
|
}
|
|
|
|
private static (string serviceName, string address, int port, string userName, string password, bool useSSL) getConfigParamsOrThrow(ModuleContext context)
|
|
{
|
|
if (context.getConfigParam() is not ConfigParam param)
|
|
throw new InvalidCastException("IConfigParam must be of type RabbitMqConnector.ConfigParam !!!");
|
|
|
|
return (
|
|
param.ServiceName ?? throw new InvalidOperationException("ServiceName is null !!!"),
|
|
param.HostName ?? throw new InvalidOperationException("HostName is null !!!"),
|
|
param.Port,
|
|
param.UserName ?? throw new InvalidOperationException("Username is null !!!"),
|
|
param.Password ?? throw new InvalidOperationException("Password is null !!!"),
|
|
param.UseSSL
|
|
);
|
|
}
|
|
|
|
public bool isValidPacketNamespace(string toCheckNamespace, IPacketCommand packetCommand)
|
|
{
|
|
var packet_namespace = ".PacketHandler";
|
|
|
|
if (null != toCheckNamespace
|
|
&& true == toCheckNamespace.Contains(packet_namespace))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ServerCore.Log.getLogger().error($"Invalid PacketNamespace !!!, not included Namespace : {packet_namespace} ⊆ {toCheckNamespace}, packetCommnad:{packetCommand.toBasicString()}");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public async Task<Result> startModule()
|
|
{
|
|
var err_msg = string.Empty;
|
|
var result = new Result();
|
|
|
|
var module_context = getModuleContext();
|
|
var config_param = module_context.getConfigParam() as ConfigParam;
|
|
NullReferenceCheckHelper.throwIfNull(config_param, () => $"config_param is null !!!");
|
|
var fn_receiver = config_param.fnServerMessageRecvFromConsumer;
|
|
NullReferenceCheckHelper.throwIfNull(fn_receiver, () => $"fn_receiver is null !!!");
|
|
|
|
result = onCreateExchangeChannel();
|
|
if(result.isFail())
|
|
{
|
|
err_msg = $"Failed to onCreateExchangeChannel() in startModule() !!! - {config_param.toBasicString()}";
|
|
result.setFail(ServerErrorCode.RabbitMqChannelCreateFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var is_success = await startMessageReceiverByConsumer(fn_receiver);
|
|
if(false == is_success)
|
|
{
|
|
err_msg = $"Failed to startMessageReceiverByConsumer() in startModule() !!! - {config_param.toBasicString()}";
|
|
result.setFail(ServerErrorCode.RabbitMqConsumerStartFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> stopModule()
|
|
{
|
|
var result = new Result();
|
|
return await Task.FromResult(result);
|
|
}
|
|
|
|
public async Task<bool> startMessageReceiverByConsumer(RabbitMqConnector.FnServerMessageRecvFromConsumer fnFromConsumer)
|
|
{
|
|
if(null == fnFromConsumer)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var result = await m_packet_receiver.registerRecvHandlerAll();
|
|
if (false == result)
|
|
{
|
|
Log.getLogger().error("rabbitMq registerRecvHandler error");
|
|
return result;
|
|
}
|
|
|
|
_fnServerMessageRecvFromConsumer = fnFromConsumer;
|
|
|
|
return base.startConsumer();
|
|
}
|
|
|
|
public async Task<bool> onRecvProtocol<T>(BasicDeliverEventArgs ea, T recvProtocol)
|
|
where T : Google.Protobuf.IMessage
|
|
{
|
|
Log.getLogger().info($"receive:{recvProtocol.ToString()} - {toBasicString()}");
|
|
|
|
Stopwatch? stopwatch = null;
|
|
var event_tid = string.Empty;
|
|
|
|
var server_logic = ServerLogicApp.getServerLogicApp();
|
|
var server_config = server_logic.getServerConfig();
|
|
|
|
if (true == server_config.PerformanceCheckEnable)
|
|
{
|
|
event_tid = System.Guid.NewGuid().ToString("N");
|
|
stopwatch = Stopwatch.StartNew();
|
|
}
|
|
|
|
var receiver = getPacketReceiver();
|
|
|
|
if (false == receiver.fillupRabbitMqPacketCommand(ea, recvProtocol, out var fillupPacketCommand))
|
|
{
|
|
Log.getLogger().error($"Invalid received Packet !!! : {recvProtocol.ToString()}");
|
|
return false;
|
|
}
|
|
|
|
if (fillupPacketCommand == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var found_handler = receiver.findRecvHandler(fillupPacketCommand);
|
|
if (null == found_handler)
|
|
{
|
|
Log.getLogger().error($"Unacceptable Packet !!! : {fillupPacketCommand.toBasicString()} - {toBasicString()}");
|
|
return false;
|
|
}
|
|
|
|
var check_by_exchange_type = onCheckByExchangeType(ea, recvProtocol);
|
|
if (false == check_by_exchange_type) return false;
|
|
|
|
//var task = Task.Factory.StartNew(async () =>
|
|
var task = Task.Run(async () =>
|
|
{
|
|
var result = await found_handler.onCheckValid(this, recvProtocol);
|
|
if (result.isFail())
|
|
{
|
|
Log.getLogger().error($"Failed to check Valid !!! : {fillupPacketCommand.toBasicString()}");
|
|
}
|
|
|
|
result = await found_handler.onProcessPacket(this, recvProtocol);
|
|
if (result.isFail())
|
|
{
|
|
Log.getLogger().error($"Failed to process Packet !!! : {fillupPacketCommand.toBasicString()}");
|
|
}
|
|
|
|
if (null != stopwatch)
|
|
{
|
|
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
|
stopwatch.Stop();
|
|
Log.getLogger().debug($"{GetType()} MQSSPacket Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{elapsed_msec} - {recvProtocol.toBasicString()}");
|
|
}
|
|
});
|
|
|
|
return await Task.FromResult(true);
|
|
}
|
|
|
|
|
|
protected override async void onRecvJsonMessageFromConsumer(object? sender, BasicDeliverEventArgs ea)
|
|
{
|
|
var body = ea.Body.ToArray();
|
|
var bodyStr = Encoding.UTF8.GetString(body);
|
|
|
|
ServerMessage? message = null;
|
|
try
|
|
{
|
|
message = ServerMessage.Parser.ParseJson(bodyStr);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.getLogger().error($"consumer parsing error bodyStr : {bodyStr}, stackTrace : {e.StackTrace}");
|
|
return;
|
|
}
|
|
|
|
if(message == null)
|
|
{
|
|
Log.getLogger().error($"message is null, bodyStr : {bodyStr}");
|
|
return;
|
|
}
|
|
|
|
if(_fnServerMessageRecvFromConsumer == null)
|
|
{
|
|
Log.getLogger().error("_fnServerMessageRecvFromConsumer is null");
|
|
return;
|
|
}
|
|
|
|
await _fnServerMessageRecvFromConsumer.Invoke(ea, message);
|
|
}
|
|
|
|
protected override void onRecvProtoMessageFromConsumer(object? sender, BasicDeliverEventArgs ea)
|
|
{
|
|
}
|
|
|
|
protected virtual bool onCheckByExchangeType<T>(BasicDeliverEventArgs ea, T recvProtocol)
|
|
where T : Google.Protobuf.IMessage
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public Task<object>? SendKick( string destServer, string name, Int32 delayMS
|
|
, Action<Task<object>>? callback )
|
|
{
|
|
int reqId = nextReqId();
|
|
Task<object>? waitTask = null;
|
|
|
|
if (callback != null)
|
|
{
|
|
CancellationTokenSource cancelTokenSrc = new CancellationTokenSource(delayMS);
|
|
|
|
waitTask = registerCompletionSource(reqId, cancelTokenSrc.Token, callback);
|
|
if (waitTask == null)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
ServerMessage message = new ServerMessage();
|
|
message.KickReq = new ServerMessage.Types.KickReq();
|
|
message.KickReq.ReqId = reqId;
|
|
message.KickReq.Name = name;
|
|
|
|
SendMessage(destServer, message);
|
|
|
|
return waitTask;
|
|
}
|
|
|
|
public void SendMessage(string to, ServerMessage message)
|
|
{
|
|
IConnection? con = getConnection();
|
|
if(con == null)
|
|
{
|
|
Log.getLogger().error("GetConnection return null");
|
|
return;
|
|
}
|
|
using (var channel = con.CreateModel())
|
|
{
|
|
Stopwatch? stopwatch = null;
|
|
var event_tid = string.Empty;
|
|
|
|
var server_logic = ServerLogicApp.getServerLogicApp();
|
|
var server_config = server_logic.getServerConfig();
|
|
|
|
if (true == server_config.PerformanceCheckEnable)
|
|
{
|
|
event_tid = System.Guid.NewGuid().ToString("N");
|
|
stopwatch = Stopwatch.StartNew();
|
|
}
|
|
|
|
channel.QueueDeclare( queue: to,
|
|
durable: true,
|
|
exclusive: false,
|
|
autoDelete: true,
|
|
arguments: null );
|
|
|
|
message.MessageTime = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.UtcNow);
|
|
message.MessageSender = getServiceName();
|
|
|
|
string messageJson = JsonFormatter.Default.Format(message);
|
|
var body = Encoding.UTF8.GetBytes(messageJson);
|
|
|
|
channel.BasicPublish( exchange: "",
|
|
routingKey: to,
|
|
basicProperties: null,
|
|
body: body );
|
|
|
|
Log.getLogger().info($"send to MQS !!!, msg:{messageJson} - receiver:{to}");
|
|
|
|
if (null != stopwatch)
|
|
{
|
|
var elapsed_msec = stopwatch.ElapsedMilliseconds;
|
|
stopwatch.Stop();
|
|
Log.getLogger().debug($"{GetType()} SMQSPacket Stopwatch Stop : ETID:{event_tid}, ElapsedMSec:{stopwatch.ElapsedMilliseconds} - {message.toBasicString()}");
|
|
}
|
|
}
|
|
}
|
|
|
|
protected abstract Result onCreateExchangeChannel();
|
|
|
|
public PacketReceiver getPacketReceiver() => m_packet_receiver;
|
|
|
|
public ModuleContext getModuleContext() => m_module_context;
|
|
|
|
public virtual string toBasicString()
|
|
{
|
|
return $"{this.getTypeName()}";
|
|
}
|
|
}
|