Ejemplo n.º 1
0
        /// <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);
        }
Ejemplo n.º 2
0
        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);
        }