/// <summary> /// 尝试发送消息 /// </summary> /// <param name="producer">生产者实例</param> /// <param name="message">消息实例</param> /// <param name="parameter">发送所需参数</param> /// <param name="key">消息的唯一标识</param> /// <param name="errorTimes">本系统自己自己执行时发现出错后,记录的错误次数(此参数不适用于生产次数)</param> /// <returns></returns> protected SendResultONS TryToSend(IONSProducer producer, Message message, object parameter, string key, int errorTimes) { SendResultONS sendResultONS = null; try { sendResultONS = producer.send(message, parameter); } catch (Exception e) { //错误计数累加 errorTimes++; //无论是阿里云服务不可用,还是生产者挂了,一共3次机会,即执行1次,然后最多重试两次 if (errorTimes < 3) { //如果生产者挂了 if (e.ToString().IndexOf("Your producer has been shutdown.") >= 0) { //置空producer producer = null; //重新获取producer并启动 producer = GetProducer(); //等待3秒 System.Threading.Thread.Sleep(3000); } else { //如果是阿里云服务不可用,等待1秒 System.Threading.Thread.Sleep(1000); } //递归 sendResultONS = TryToSend(producer, message, parameter, key, errorTimes); } else { //重试2次都还是失败 string className = this.GetType().Name; string methodName = "Process"; string errorMessage = _Environment + "." + _ApplicationAlias + "." + className + "." + methodName + "尝试发送时,出现了第" + errorTimes + "次出错,key=" + key + ":" + e.ToString(); //记录本地错误日志 DebugUtil.Debug(errorMessage); //记录FATAL日志 ONSHelper.SaveLog(LogTypeEnum.FATAL, className, methodName, errorMessage); //发送邮件 ONSHelper.SendDebugMail(_Environment + "." + _ApplicationAlias + "." + className + "." + methodName + "尝试发送时出错", errorMessage); //抛出异常 throw new Exception(errorMessage); } } return(sendResultONS); }
public static bool React(Message value, Type classType) { bool needToCommit = false; string failureReason = ""; string topic = ""; string tag = ""; string pid = ""; string cid = ""; string key = ""; string type = ""; string body = ""; string method = ""; string requestTraceId = ""; string shardingKey = ""; Enum topicTag = null; int consumedTimes = 0; //尝试找到消费者服务类实例来消费 try { topic = value.getTopic(); tag = value.getTag(); //pid = "PID_" + value.getTopic().ToUpper(); pid = "GID_" + value.getTopic().ToUpper(); //cid = ("CID_" + topic + "_" + _ApplicationAlias + "_" + classType.Name).ToUpper(); cid = ("GID_" + topic + "_" + _ApplicationAlias + "_" + classType.Name).ToUpper(); key = value.getKey(); type = value.getUserProperties("type"); body = value.getMsgBody(); body = Base64Util.Decode(body); requestTraceId = value.getUserProperties("requestTraceId") ?? ""; shardingKey = value.getUserProperties("shardingKey") ?? ""; //由于CallContext中不存在TraceId,则直接添加,以便在非http请求环境下获取到TraceId CallContext.LogicalSetData("TraceId", requestTraceId); object parameter; RedisTool RT = new RedisTool(_AliyunOnsRedisDbNumber, _RedisExchangeHosts); if (RT != null) { string antirepeatKey = key + "_" + cid + "_antirepeat"; DateTime dateTime = DateTime.Now; TimeSpan timeSpan = dateTime.AddSeconds(1) - dateTime; bool setResult = RT.StringSet(antirepeatKey, "1", timeSpan, When.NotExists); if (!setResult) { //如果设置失败,则说明key已经存在,本消息属于重复消费,直接返回true,不执行后面的消费逻辑 return(true); } /* * //在ONSConsumerServiceList中找到能匹配TopicTag的消费者服务类实例 * object service = ONSHelper.ONSConsumerServiceList.Where(s => * { * string className = s.GetType().Name; * IAbstractConsumerService iservice = (IAbstractConsumerService)s; * Enum[] topicTagList = iservice.TopicTagList; * if (topicTagList != null) * { * //需要同时判断topic和tag都匹配 * topicTag = topicTagList.Where(tt => * { * string serviceTopic = (_Environment + "_" + tt.GetType().Name).ToUpper(); * string serviceTag = tt.ToString(); * return (topic.ToUpper() == serviceTopic) && (tag.ToUpper() == serviceTag.ToUpper()); * }).FirstOrDefault(); * * if (topicTag != null) * { * return true; * } * } * return false; * }).FirstOrDefault(); * //*/ object service = ONSHelper.ONSConsumerServiceList.Where(s => s.GetType().Name == classType.Name).FirstOrDefault(); //如果消费者服务类实例存在则消费消息 if (service != null) { //获取消费服务类的核心方法(即开发者自己实现的方法) method = service.GetType().FullName + ".ProcessCore"; //获取内部方法(此方法是受保护的,因此获取MethodInfo复杂一些) MethodInfo methodInfo = service.GetType().GetMethod("InternalProcess", BindingFlags.NonPublic | BindingFlags.Instance); //获取参数列表,实际就一个泛型T参数 ParameterInfo[] parameterInfos = methodInfo.GetParameters(); //判断类型 if (parameterInfos[0].ParameterType.ToString().ToLower() == "system.string") { //string类型 parameter = body; } else { //自定义类型 parameter = JsonConvert.DeserializeObject(body, parameterInfos[0].ParameterType); } //执行InternalProcess方法 needToCommit = (bool)methodInfo.Invoke(service, new object[] { parameter }); if (needToCommit == false) { failureReason = method + "执行返回false,可能是该方法逻辑上返回false,也可能是该方法执行时它自己捕捉到错误返回false"; } } else { //找不到消费者实例对象 DebugUtil.Debug("MESSAGE_KEY:" + key + ",找不到消费者实例,topic:" + topic + ",tag:" + tag + ""); } } else { failureReason = "尝试通过redis写入“key来做避免重复消费的判断”时,无法实例化redis工具类,可能是redis服务暂不可用。"; } } catch (Exception e) { failureReason = "尝试消费时,key=" + key + ",捕获异常:" + e.ToString(); //DebugUtil.Debug(e.ToString()); } //*/ //尝试记录消费信息 try { //获取redis客户端工具实例 RedisTool RT = new RedisTool(_AliyunOnsRedisDbNumber, _RedisExchangeHosts); if (RT != null) { string consumedTimesKey = key + "_" + cid + "_consumedtimes"; //设置消费次数并自增 consumedTimes = (int)RT.StringIncrement(consumedTimesKey); //获取过期时间 TimeSpan?timeSpan = RT.KeyTimeToLive(consumedTimesKey); if (timeSpan == null) { //没设置过过期时间,则设置过期时间 RT.KeyExpire(consumedTimesKey, TimeSpan.FromSeconds(_AliyunOnsRedisServiceResultExpireIn)); } } else { failureReason = "尝试通过redis更新生产方法执行次数时,无法实例化redis工具类,可能是redis服务暂不可用。"; } } catch (Exception e) { failureReason = "尝试通过redis更新生产方法执行次数时,捕捉异常:" + e.ToString(); //DebugUtil.Debug(e.ToString()); } finally { try { //写ConsumerData数据 ConsumerData consumerData = new ConsumerData(requestTraceId); //string data; consumerData.ApplicationAlias = _ApplicationAlias; consumerData.Accomplishment = needToCommit; consumerData.Topic = topic; consumerData.Tag = tag; consumerData.ProducerId = pid; consumerData.ConsumerId = cid; consumerData.Key = key; consumerData.Type = type; consumerData.Message = body; consumerData.Method = method; consumerData.FailureReason = failureReason; consumerData.ConsumedStatus = needToCommit ? "Commit" : "Reconsume"; consumerData.ConsumedTimes = consumedTimes; consumerData.ShardingKey = shardingKey; consumerData.ServerIp = ONSHelper.GetServerIp(); NestDataHelper.WriteData(consumerData); } catch (Exception e) { ONSHelper.SendDebugMail(_Environment + "." + _ApplicationAlias + "环境发送下游消费日志失败", "消息key:" + key + ",错误信息如下:" + e.ToString()); } } return(needToCommit); }