//验证接收者 private static void ValidateReceiver(string receivers, ref List <string> receiver_valid, ref List <string> receiver_invalid) { if (receiver_valid == null) { receiver_valid = new List <string>(); } if (receiver_invalid == null) { receiver_invalid = new List <string>(); } if (String.IsNullOrEmpty(receivers)) { return; } var receivers_all = receivers.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var redis_userinfo = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_USER_KEY); foreach (var userid in receivers_all) { if (redis_userinfo.Exec <bool>(db => db.KeyExists($"im_userinfo_{userid}"))) { receiver_valid.Add(userid); } else { receiver_invalid.Add(userid); } } }
private static void SlaveMode() { if (!_instanceId.HasValue) { _instanceId = 0; } //定时保存日志 if (enabledLogFile) { _globalTimer = new Timer( state => SaveLogToFile(), null, 0, //立即启动定时器 1000 * 60 //1分钟保存一次日志 ); } //检查一下是否有历史的待发送消息 var redis_msgmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); var msgcount = redis_msgmq.Exec <long>(db => db.ListLength(MessageSendStateQueue.SENDING)); if (msgcount > 0) { //发送历史待处理消息 Task.Factory.StartNew(ProcessHistoryMessage); } //启动订阅通知接收器,以便能即时获知有新消息到来 try { _globalSubscriber = FaceHand.Common.ResdisExecutor.GetConn(RedisConfig.REDIS_MSGMQ_KEY).GetSubscriber(); _globalSubscriber.Subscribe(RedisChannelName.NEW_MESSAGE_CHANNEL_TOPULLER, NotifyChannelHandler); WriteLog($"PostService Instance {_instanceId.Value} Is Started"); new Timer( state => NotifyChannelStateCheck_Slave(), null, 0, //立即启动定时器 1000 * 5 //5秒检测一次推送通道是否还存活,如果没有存活就尝试重启当前进程 ); } catch (Exception ex) { WriteErrorLog("PostService Boot Failed." + ex.Message); } Console.ReadLine(); // if (_globalTimer != null) { _globalTimer.Dispose(); } if (_globalSubscriber != null) { _globalSubscriber.UnsubscribeAll(); } }
//处理历史消息 private static void ProcessHistoryMessage() { var redis_msgmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); var msguid_list = redis_msgmq.Exec <RedisValue[]>(db => db.ListRange(MessageSendStateQueue.SENDING, 0, -1)); foreach (RedisValue msg_uid in msguid_list) { if (!msg_uid.IsNullOrEmpty) { try { SendMsg(msg_uid, true); } catch (Exception ex) { WriteErrorLog($"post message failed. {ex.Message}"); } } } }
//投送消息 private static void SendMsg(string msg_uid, bool isRemoveFromMQ) { //获取消息以验证消息的有效性 var msg = IMRedisDAL.GetMsg(msg_uid); if (msg == null || msg.Count == 0) { throw new BusinessException($"message {msg_uid} not found"); } var state = msg.GetStringValue(RedisFields.MSG_STATE).AsEnum <MsgSendState>(); if (state != MsgSendState.Sending) { //消息ID被取到,但是又不是正在发送的状态 //说明消息已被其它进程处理,这时应该从消息队列中删除这个ID var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget)); } else { //验证接收人,把有效的人和无效的人分离出来 var receiver_valid = new List <string>(); var receiver_invalid = new List <string>(); ValidateReceiver(msg[RedisFields.MSG_RECEIVERS], ref receiver_valid, ref receiver_invalid); //没有有效接收人,消息发送失败 if (receiver_valid.Count == 0) { //更新消息发送状态为失败 FaceHand.Common.ResdisExecutor.ExecCommand(db => db.HashSet($"msg_{msg_uid}", new HashEntry[] { //更新发送失败的人 new HashEntry(RedisFields.MSG_FAILED_RECEIVERS, String.Join(",", receiver_invalid)), //更新消息发送状态 new HashEntry(RedisFields.MSG_STATE, (int)MsgSendState.Failed), //更新消息发送状态 new HashEntry(RedisFields.MSG_ERROR_MSG, "available receivers count is zero") }, CommandFlags.FireAndForget), RedisConfig.REDIS_MSGCACHE_KEY); //从队列中删除消息 if (isRemoveFromMQ) { var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget)); } //抛出错误,以便控制台能看到发送失败 throw new BusinessException($"available receivers count is zero"); } else { //投送消息 var redis_msgpost = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGPOST_KEY); redis_msgpost.Exec(db => { var batch = db.CreateBatch(); var msg_sendtime = msg.GetStringValue(RedisFields.MSG_SEND_TIME).AsLong(); var msg_senderid = msg.GetStringValue(RedisFields.MSG_SEND_USERID); var msg_type = (int)msg.GetStringValue(RedisFields.MSG_TYPE).AsEnum <MsgType>(); var isChatMsg = msg_type < 60; var now = DateTime.Now; var nowUnixTimestmp = now.AsUnixTimestamp(); foreach (var receiver_userid in receiver_valid) { //更新未读消息数,未读消息数按消息类型分别存 batch.HashIncrementAsync($"notreadcount_{receiver_userid}", msg[RedisFields.MSG_TYPE], 1D, CommandFlags.FireAndForget); //将消息ID放入对应人的未读消息清单 batch.SortedSetAddAsync($"notreadlist_{receiver_userid}", msg_uid, nowUnixTimestmp, When.Always, CommandFlags.FireAndForget); //单独统计聊天消息的数量 if (isChatMsg) { var chatnotreadcount_k = $"chatnotreadcount_{msg_senderid}_{receiver_userid}"; var chatnotreadcount = db.Execute("GET", chatnotreadcount_k).AsInt(); if (chatnotreadcount < 0) { batch.ExecuteAsync("SET", 1); } else { batch.ExecuteAsync("INCRBY", chatnotreadcount_k, 1D); } } } batch.Execute(); }); //更新消息发送状态 FaceHand.Common.ResdisExecutor.ExecCommand(db => db.HashSet($"msg_{msg_uid}", new HashEntry[] { //更新发送成功的人 new HashEntry(RedisFields.MSG_SUCCESS_RECEIVERS, String.Join(",", receiver_valid)), //更新发送失败的人 new HashEntry(RedisFields.MSG_FAILED_RECEIVERS, String.Join(",", receiver_invalid)), //更新消息发送状态 new HashEntry(RedisFields.MSG_STATE, (int)MsgSendState.Success) }, CommandFlags.FireAndForget), RedisConfig.REDIS_MSGCACHE_KEY); var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); //从队列中删除消息 if (isRemoveFromMQ) { redis_msmq.Exec(db => db.ListRemove(MessageSendStateQueue.SENDING, msg_uid, 0, CommandFlags.FireAndForget)); } //通过订阅器通知客户端有新消息 if (receiver_valid.Count > 0) { Task.Factory.StartNew(() => { try { redis_msmq.Exec(db => db.Publish( RedisChannelName.NEW_MESSAGE_CHANNEL_TOCLIENT, $"{msg_uid},{String.Join(",", receiver_valid)}", //通知client manager CommandFlags.FireAndForget)); } catch (Exception ex) { WriteErrorLog($"notify {msg_uid} to client failed. {ex.Message}"); } }); } //将消息持久化到数据库 Task.Factory.StartNew(() => { try { using (var context = new DataContext(MysqlDbConfig.IMCONN)) { IMMySqlDAL.Instance.InsertMsg(msg, receiver_valid); } } catch (Exception ex) { WriteErrorLog($"save message {msg_uid} to database failed. {ex.Message}"); } }); //如果是应用消息,还要考虑是否通知到企业微信端 if (msg[RedisFields.MSG_TYPE].AsEnum <MsgType>() == MsgType.system_app) { } WriteLog($"post message success。msg_uid:{msg_uid}"); } } }
private static void NotifyChannelHandler(RedisChannel channel, RedisValue message) { //WriteLog($"{channel}: {message}"); if (_globalSubscriber != null) { switch (message) { case ConstDefined.PUBCMD_CLOSE: { try { _globalSubscriber.Unsubscribe(channel); } catch (Exception ex) { WriteErrorLog($"unsubscribe failed. {ex.Message}"); } break; } case ConstDefined.PUBCMD_CLOSEALL: { try { _globalSubscriber.UnsubscribeAll(); } catch (Exception ex) { WriteErrorLog($"unsubscribe failed. {ex.Message}"); } break; } case ConstDefined.PUBCMD_AREYOUOK: { _slave_subscriber_lastActiveTime = DateTime.Now; //WriteLog("AreYouOk Messages"); break; } case ConstDefined.PUBCMD_SEND: { try { //从消息队列中取最后一下待发送消息 var redis_msmq = new FaceHand.Common.ResdisExecutor(RedisConfig.REDIS_MSGMQ_KEY); var msg_uid = redis_msmq.Exec <RedisValue>(db => db.ListRightPop(MessageSendStateQueue.SENDING)); if (msg_uid.HasValue && !msg_uid.IsNullOrEmpty) { SendMsg(msg_uid, false); } } catch (Exception ex) { WriteErrorLog($"post message failed. {ex.Message}"); } break; } } } }