Example #1
0
        /// <summary>
        /// 初始化ONS事务消息相关对象,一般在Application_Start中调用
        /// </summary>
        public static void Initialize()
        {
            if (_AliyunOnsIsEnabled == "1")
            {
                try
                {
                    //初始化服务类列表和服务类标签列表
                    InitializeProperties();

                    //判断生产者的服务类是否存在
                    if (ONSProducerServiceList.Count > 0)
                    {
                        //输出生产服务类类名
                        ONSProducerServiceList.ForEach(service => DebugUtil.Debug("生产者的服务类:" + service.GetType().FullName));

                        //判断生产者实例列表是否为空
                        if (ONSProducerList != null && ONSProducerList.Count > 0)
                        {
                            ONSProducerList.ForEach(producer =>
                            {
                                //启动上游事务生产者
                                producer.start();
                                DebugUtil.Debug("Topic:" + producer.Topic + ",ProducerId(" + producer.Type.ToString() + @"):" + producer.ProducerId + @"生产者.start()");
                            });
                        }
                    }

                    //判断消费者的服务类是否存在
                    if (ONSConsumerServiceList.Count > 0)
                    {
                        //输出消费服务类类名
                        ONSConsumerServiceList.ForEach(service => DebugUtil.Debug("消费者的服务类:" + service.GetType().FullName));

                        //判断消费者实例列表是否为空
                        if (ONSConsumerList != null && ONSConsumerList.Count > 0)
                        {
                            ONSConsumerList.ForEach(consumer =>
                            {
                                string tags = GetConsumerServiceTags(consumer.TagList);
                                consumer.subscribe(consumer.Topic, tags);
                                //启动上游事务生产者
                                consumer.start();
                                DebugUtil.Debug("Topic:" + consumer.Topic + ",ConsumerId(" + consumer.Type.ToString() + @"):" + consumer.ConsumerId + @"消费者.start(),topic:" + consumer.Topic + ",tags:" + tags);
                            });
                        }
                    }

                    //延时若干毫秒
                    Thread.Sleep(1000);
                    DebugUtil.Debug(_Environment + "." + _ApplicationAlias + ".ONSHelper.Initialize最后延迟1000毫秒");
                }
                catch (Exception e)
                {
                    //记录本地错误日志
                    DebugUtil.Debug(_Environment + "." + _ApplicationAlias + ".ONSHelper.Initialize出错:" + e.ToString());

                    //记录FATAL日志,发送FATAL目前出错
                    //SaveLog(LogTypeEnum.FATAL, "ONSHelper", "Initialize", _Environment + "." + _ApplicationAlias + ".ONSHelper.Initialize出错:" + e.ToString());

                    //发送邮件
                    SendDebugMail(_Environment + "." + _ApplicationAlias + ".ONSHelper.Initialize出错", ":" + e.ToString());
                }
            }
        }
Example #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();
                cid            = ("CID_" + 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") ?? "";

                object parameter;

                /*
                 * //在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 + "");
                }
            }
            catch (Exception e)
            {
                failureReason = "尝试消费时,key=" + key + ",捕获异常:" + e.ToString();
                //DebugUtil.Debug(e.ToString());
            }
            //*/

            //尝试记录消费信息
            try
            {
                RedisTool RT = new RedisTool(_AliyunOnsRedisDbNumber, _RedisExchangeHosts);
                if (RT != null)
                {
                    string consumedTimesKey   = key + "_" + cid + "_consumedtimes";
                    string consumedTimesValue = RT.StringGet(consumedTimesKey);

                    if (string.IsNullOrEmpty(consumedTimesValue))
                    {
                        //不存在key,则新增
                        consumedTimes++;
                        bool isSaved = RT.StringSet(consumedTimesKey, consumedTimes, TimeSpan.FromSeconds(_AliyunOnsRedisServiceResultExpireIn));
                        if (!isSaved)
                        {
                            //设置消费次数失败
                            failureReason = "设置消费次数失败。";
                        }
                    }
                    else
                    {
                        //存在key,则递增
                        int.TryParse(consumedTimesValue, out consumedTimes);
                        consumedTimes++;
                        RT.StringIncrement(consumedTimesKey);
                    }
                }
                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;
                    NestDataHelper.WriteData(consumerData);
                }
                catch (Exception e)
                {
                    ONSHelper.SendDebugMail(_Environment + "." + _ApplicationAlias + "环境发送下游消费日志失败", "消息key:" + key + ",错误信息如下:" + e.ToString());
                }
            }

            return(needToCommit);
        }
Example #3
0
        /// <summary>
        /// 生成消费者实例
        /// </summary>
        /// <typeparam name="T">消费者服务基类类型</typeparam>
        /// <param name="assembly">消费者服务类所在程序集</param>
        /// <param name="type">消费者服务类的类型</param>
        /// <param name="messageType">消息类型BASE,ORDER,TRAN</param>
        /// <param name="func">生成消费者实例的委托代码</param>
        internal static void CreateConsumer <T>(Assembly assembly, Type type, ONSMessageType messageType, Func <ONSFactoryProperty, string, string, Type, IONSConsumer> func)
        {
            if (type.BaseType.FullName.IndexOf(typeof(T).Name) >= 0)
            {
                //添加到消费者服务类实例列表
                //ONSConsumerServiceList.Add(type);
                object service = assembly.CreateInstance(type.FullName);
                ONSConsumerServiceList.Add(service);
                //获取服务接口
                IAbstractConsumerService iservice = (IAbstractConsumerService)service;
                //获取枚举数组对象
                Enum[] topicTagList = iservice.TopicTagList;

                if (topicTagList != null && topicTagList.Length > 0)
                {
                    foreach (Enum topicTag in topicTagList)
                    {
                        string serviceTopic = topicTag.GetType().Name;
                        string serviceTag   = topicTag.ToString();
                        string className    = type.Name;

                        string topic = (_Environment + "_" + serviceTopic).ToUpper();
                        //CID修改成GID
                        //string consumerId = ("CID_" + topic + "_" + _ApplicationAlias + "_" + className).ToUpper();
                        string consumerId = ("GID_" + topic + "_" + _ApplicationAlias + "_" + className).ToUpper();

                        DebugUtil.Debug("consumerId:" + consumerId);

                        IONSConsumer consumer = ONSConsumerList.Where(c => c.Type == messageType.ToString() && c.ConsumerId == consumerId).FirstOrDefault();
                        if (consumer == null)
                        {
                            //实例化ONSFactoryProperty
                            ONSFactoryProperty onsConsumerFactoryProperty = new ONSFactoryProperty();
                            onsConsumerFactoryProperty.setFactoryProperty(ONSFactoryProperty.AccessKey, _AliyunOnsAccessKey);
                            onsConsumerFactoryProperty.setFactoryProperty(ONSFactoryProperty.SecretKey, _AliyunOnsSecretKey);
                            onsConsumerFactoryProperty.setFactoryProperty(ONSFactoryProperty.ConsumerId, consumerId);
                            //onsConsumerFactoryProperty.setFactoryProperty(ONSFactoryProperty.PublishTopics, _ONSTopic);

                            if (_AliyunOnsConsumerLogPath != "")
                            {
                                if (Directory.Exists(_AliyunOnsConsumerLogPath))
                                {
                                    onsConsumerFactoryProperty.setFactoryProperty(ONSFactoryProperty.LogPath, _AliyunOnsConsumerLogPath);
                                }
                            }

                            //获取消费者IONSConsumer
                            consumer = func(onsConsumerFactoryProperty, topic, consumerId, type);

                            //记录消费者初始化信息
                            DebugUtil.Debug("Topic:" + topic + ",ConsumerId(" + consumer.Type + "):" + consumer.ConsumerId + "消费者.new()");

                            //新增代理类ONSConsumer实例到ONSConsumerList中
                            ONSConsumerList.Add(consumer);
                        }

                        if (!consumer.TagList.Contains(serviceTag))
                        {
                            consumer.TagList.Add(serviceTag);
                        }
                    }
                }
            }
        }
Example #4
0
        public override TransactionStatus check(Message value)
        {
            Console.WriteLine("check topic: {0}, tag:{1}, key:{2}, msgId:{3},msgbody:{4}, userProperty:{5}",
                              value.getTopic(), value.getTag(), value.getKey(), value.getMsgID(), value.getBody(), value.getUserProperties("VincentNoUser"));
            // 消息 ID(有可能消息体一样,但消息 ID 不一样。当前消息 ID 在控制台无法查询)
            //string msgId = value.getMsgID();
            // 消息体内容进行 crc32, 也可以使用其它的如 MD5
            // 消息 ID 和 crc32id 主要是用来防止消息重复
            // 如果业务本身是幂等的, 可以忽略,否则需要利用 msgId 或 crc32Id 来做幂等
            // 如果要求消息绝对不重复,推荐做法是对消息体 body 使用 crc32或 md5来防止重复消息

            string            transactionType   = "Checker";
            bool              serviceResult     = false;
            TransactionStatus transactionStatus = TransactionStatus.Unknow;
            string            failureReason     = "";
            string            topic             = "";
            string            tag  = "";
            string            pid  = "";
            string            key  = "";
            string            body = "";
            string            checkerMethodParameter = "";
            string            method           = "";
            string            requestTraceId   = "";
            string            producedTimesKey = "";
            int producedTimes = 0;

            try
            {
                topic                  = value.getTopic();
                tag                    = value.getTag();
                pid                    = "PID_" + value.getTopic().ToUpper();
                key                    = value.getKey();
                body                   = value.getMsgBody();
                requestTraceId         = value.getUserProperties("requestTraceId") ?? "";
                checkerMethodParameter = Base64Util.Decode(body);
                producedTimesKey       = key + ":" + _ApplicationAlias + ":producedtimes";

                DebugUtil.Debug("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.key  " + key);

                /*在LocalTransactionExecuterExecute.execute方法中返回unknown,之后会调用LocalTransactionChecker.check方法
                 * 如果此消息topic被多个网站用作生产者,那么会导致此message会被随即传递到其他网站或当前网站中去时长调用check方法
                 * 这种传递紊乱的情况可以称为“同topic被多站点作为生产者导致check紊乱的现象”
                 * 因此如果被传递到其他网站后,需要判断当前网站是否是消息的来源网站,如果不是则返回unknown,也不记录任何记录。
                 * 如果按同topic被5个网站作为生产者,那么当execute或check出错后,能正确来到消息原始网站的概率为20%,而每次调用check历经5秒
                 * 假设是随即平均发送到5个网站随即一个中,那么平均需要历经25秒后,才能来到原始网站,因此能来到消息原始网站的平均等待时间的公式为
                 * time=n*5秒,此处n为使用该topic的网站个数。
                 * //*/
                int applicationAliasLength = _ApplicationAlias.Length;
                if (key.Substring(2, applicationAliasLength) != _ApplicationAlias)
                {
                    return(transactionStatus);
                }

                DebugUtil.Debug("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.after try...");
                DebugUtil.Debug("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.checkerMethod " + value.getUserProperties("checkerMethod"));
                DebugUtil.Debug("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.checkerMethodParameter " + value.getUserProperties("checkerMethodParameter"));

                if (ONSHelper.CheckerMethodDictionary.ContainsKey(value.getUserProperties("checkerMethod")))
                {
                    MethodInfo      methodInfo     = ONSHelper.CheckerMethodDictionary[value.getUserProperties("checkerMethod")];
                    Type            type           = methodInfo.ReflectedType;
                    Assembly        assembly       = Assembly.GetAssembly(type);
                    object          service        = assembly.CreateInstance(type.FullName);
                    ParameterInfo[] parameterInfos = methodInfo.GetParameters();

                    //DebugUtil.Log(parameterInfos[0].ParameterType.ToString());

                    method = type.FullName + ".ProcessCore";

                    //判断类型
                    if (parameterInfos[0].ParameterType.ToString().ToLower() == "system.string")
                    {
                        //string类型
                        serviceResult = (bool)methodInfo.Invoke(service, new object[] { checkerMethodParameter });
                    }
                    else
                    {
                        //自定义类型
                        object parameter = JsonConvert.DeserializeObject(checkerMethodParameter, parameterInfos[0].ParameterType);
                        serviceResult = (bool)methodInfo.Invoke(service, new object[] { parameter });
                    }

                    //DebugUtil.Log("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.body:" + body);

                    if (serviceResult)
                    {
                        // 本地事务成功则提交消息
                        transactionStatus = TransactionStatus.CommitTransaction;
                    }
                    else
                    {
                        // 本地事务失败则回滚消息
                        transactionStatus = TransactionStatus.RollbackTransaction;
                        failureReason     = method + "执行返回serviceResult为false,可能是该方法逻辑上返回serviceResult为false,也可能是该方法执行时它自己捕捉到错误返回serviceResult为false";
                    }
                }
                else
                {
                    // 不存在key则回滚消息,这里不能用RollbackTransaction,因为我们使用相同的topic在各个网站之间,这种机制会导致其他网站也会调用check方法,一旦RollbackTransaction,那么原先的网站不再重试调用check方法了,因此必须返回unknow
                    transactionStatus = TransactionStatus.Unknow;
                    failureReason     = "CheckerFuncDictionary中不存在key:" + value.getUserProperties("checkerMethod");
                    DebugUtil.Debug("MESSAGE_KEY:" + value.getKey() + ",ONSLocalTransactionChecker.check.error:CheckerFuncDictionary中不存在key:" + value.getUserProperties("checkerMethod"));
                }

                //在写log之前先做base64解密
                body = Base64Util.Decode(body);

                //事务已经执行,尝试通过redis更新生产方法执行次数,此处如果出错是可以容忍的,但是得把错误信息记录到ProduceData中
                RedisTool RT = new RedisTool(_AliyunOnsRedisDbNumber, _RedisExchangeHosts);
                if (RT != null)
                {
                    try
                    {
                        //获取已经生产次数
                        string producedTimesValue = RT.StringGet(producedTimesKey);
                        //如果取不到
                        if (string.IsNullOrEmpty(producedTimesValue))
                        {
                            //不存在key,则新增
                            producedTimes++;
                            bool isSaved = RT.StringSet(producedTimesKey, producedTimes, TimeSpan.FromSeconds(_AliyunOnsRedisServiceResultExpireIn));
                            if (!isSaved)
                            {
                                transactionStatus = TransactionStatus.Unknow;
                            }
                        }
                        else
                        {
                            //存在key,则递增
                            int.TryParse(producedTimesValue, out producedTimes);
                            producedTimes++;
                            RT.StringIncrement(producedTimesKey);
                        }
                    }
                    catch (Exception e)
                    {
                        throw new Exception("事务已经执行,返回" + serviceResult + "。但是尝试通过redis更新生产方法执行次数时,捕捉异常:" + e.ToString());
                    }
                }
                else
                {
                    //redis有问题,导致无法实例化工具类
                    throw new Exception("事务已经执行,返回" + serviceResult + "。但是尝试通过redis更新生产方法执行次数时,无法实例化redis工具类,可能是redis服务暂不可用。");
                }
            }
            catch (Exception e)
            {
                failureReason = "事务消息在执行Checker.check方法时捕获异常:" + e.ToString();
                DebugUtil.Debug(e.ToString());
            }
            finally
            {
                ProducerData producerData = new ProducerData(requestTraceId);
                producerData.Accomplishment   = transactionStatus != TransactionStatus.Unknow;
                producerData.ApplicationAlias = _ApplicationAlias;
                producerData.Topic            = topic;
                producerData.Tag               = tag;
                producerData.ProducerId        = pid;
                producerData.Key               = key;
                producerData.Type              = ONSMessageType.TRAN.ToString();
                producerData.Message           = checkerMethodParameter;
                producerData.TransactionType   = transactionType;
                producerData.Method            = method;
                producerData.ServiceResult     = serviceResult;
                producerData.TransactionStatus = transactionStatus.ToString();
                producerData.FailureReason     = failureReason;
                producerData.ProducedTimes     = producedTimes;
                producerData.ShardingKey       = "";
                NestDataHelper.WriteData(producerData);
            }

            return(transactionStatus);
        }