zoukankan      html  css  js  c++  java
  • webapi+Task并行请求不同接口实例

      标题的名称定义不知道是否准确,不过我想表达的意思就是使用Task特性来同时请求多个不同的接口,然后合并数据;我想这种场景的开发对于对接过其他公司接口的人不会陌生,本人也是列属于之内,更多的是使用最原始的异步委托的方法去处理,今天抽空写了一个使用4.5新特性Task来处理这种场景;各位看客有什么疑问或者好的建议及分享请博客通知,谢谢。

      A.项目结构图

      

      B.namespace Pm.V.PM_BLL下面的BaseClass定义如下:

     1  public abstract class BaseClass
     2     {
     3 
     4         #region 初始化xml配置信息 BaseClass
     5 
     6         /// <summary>
     7         /// 初始化xml配置信息
     8         /// </summary>
     9         /// <param name="xmlConf"></param>
    10         public BaseClass(string xmlConfigPath)
    11         {
    12 
    13             try
    14             {
    15 
    16                 if (string.IsNullOrEmpty(xmlConfigPath))
    17                 {
    18 
    19                     //默认各个Xml配置
    20                     var defaultConfigFolder = "PluginXml";
    21                     var baseAddr = AppDomain.CurrentDomain.BaseDirectory;
    22                     xmlConfigPath = Path.Combine(baseAddr, defaultConfigFolder, this.GetType().Name + ".xml");
    23                 }
    24 
    25                 XmlDocument doc = new XmlDocument();
    26                 doc.Load(xmlConfigPath);
    27 
    28                 Config = new BaseConfig();
    29                 Config.Url = doc.SelectSingleNode("//Pm/Url") == null ? "" : doc.SelectSingleNode("//Pm/Url").InnerXml;
    30                 Config.UserName = doc.SelectSingleNode("//Pm/UserName") == null ? "" : doc.SelectSingleNode("//Pm/UserName").InnerXml;
    31                 Config.UserKey = doc.SelectSingleNode("//Pm/UserKey") == null ? "" : doc.SelectSingleNode("//Pm/UserKey").InnerXml;
    32 
    33                 Config.Doc = doc;
    34             }
    35             catch (Exception ex)
    36             {
    37 
    38                 throw new Exception(ex.Message);
    39             }
    40 
    41         }
    42 
    43         /// <summary>
    44         /// xml配置信息
    45         /// </summary>
    46         public BaseConfig Config;
    47         #endregion
    48 
    49         #region  获取文章信息 _GetArticles  +BaseResponse
    50 
    51         /// <summary>
    52         /// 获取文章信息
    53         /// </summary>
    54         /// <param name="request"></param>
    55         /// <returns></returns>
    56         public virtual MoArticlesResponse _GetArticles(object request)
    57         {
    58             return null;
    59         }
    60         #endregion
    61 
    62     }
    63 
    64     /// <summary>
    65     /// xml配置文件信息
    66     /// </summary>
    67     public class BaseConfig
    68     {
    69 
    70         /// <summary>
    71         /// 接口地址
    72         /// </summary>
    73         public string Url { get; set; }
    74 
    75         /// <summary>
    76         /// 账号
    77         /// </summary>
    78         public string UserName { get; set; }
    79 
    80         /// <summary>
    81         /// 密码|秘钥
    82         /// </summary>
    83         public string UserKey { get; set; }
    84 
    85         /// <summary>
    86         /// xml文件全部信息
    87         /// </summary>
    88         public XmlDocument Doc { get; set; }
    89 
    90     }
    View Code

      主要是在实例的时候读取各个业务模块的配置文件,初始化一些常用并且是公共的属性信息;其次创建了一个虚方法_GetArticles,注意里面的参数是object这个将再后面的时候重提;

      C.下面就是请求第三方业务实现类的代码,这里测试的时候分别使用

        1.CnblogsClass类抓取博客园首页的博客列表信息

      1    /// <summary>
      2     /// 博客园信息(如果涉及到信息来源问题,请及时联系开源作者,谢谢)
      3     /// </summary>
      4     public class CnblogsClass : BaseClass
      5     {
      6         /// <summary>
      7         /// 初始化xml配置
      8         /// </summary>
      9         public CnblogsClass() : base("") { }
     10 
     11         #region  获取文章信息 _GetArticles  +BaseResponse
     12 
     13         /// <summary>
     14         /// 获取文章信息
     15         /// </summary>
     16         /// <param name="request"></param>
     17         /// <returns></returns>
     18         public override MoArticlesResponse _GetArticles(object obj)
     19         {
     20             #region 初始化信息
     21 
     22             var request = new MoArticlesRequest();
     23             var response = new MoArticlesResponse();
     24             var sbLog = new StringBuilder(string.Empty);
     25             var url = this.Config.Url;   //这里获取配置文件的url
     26             #endregion
     27 
     28             try
     29             {
     30 
     31                 #region 接口验证数据
     32 
     33                 request = obj as MoArticlesRequest;
     34                 if (request == null)
     35                 {
     36 
     37                     response.ErrorCode = (int)EHelper.PmException.获取数据为空;
     38                     response.ErrorMsg = EHelper.PmException.获取数据为空.ToString();
     39                     return response;
     40                 }
     41 
     42                 #endregion
     43 
     44                 #region 请求数据
     45 
     46 
     47                 sbLog.AppendFormat("请求地址:{0}
    ", url);
     48                 var result = PublicClass._HttpGet(url);   //这里一般都是post数据到第三方接口,测试没有第三方可以使用,所以使用抓取博客园首页数据
     49                 sbLog.AppendFormat("返回信息:{0}
    ", result);
     50                 #endregion
     51 
     52                 #region 解析
     53 
     54                 //使用正则解析数据
     55                 var rgs = Regex.Matches(result, "class="titlelnk"\s+href="(?<link>http://www(\w|\.|\/)+\.html)"[^>]+>(?<title>[^<]+)<\/a>[^D]+a>(?<des>[^<]+)[^D]+lightblue">(?<author>\w+)<\/a>[^D]+发布于(?<publishtime>\s+(\d|-|\s|:)+)[^<]+");
     56 
     57                 if (rgs.Count <= 0)
     58                 {
     59 
     60                     response.ErrorCode = (int)EHelper.PmException.获取数据为空;
     61                     response.ErrorMsg = EHelper.PmException.获取数据为空.ToString();
     62                     return response;
     63                 }
     64 
     65                 foreach (Match item in rgs)
     66                 {
     67 
     68                     var article = new MoArticle();
     69 
     70                     article.Author = item.Groups["author"].Value;
     71                     article.LinkUrl = item.Groups["link"].Value;
     72                     article.Title = item.Groups["title"].Value;
     73                     article.PublishTime = item.Groups["publishtime"].Value;
     74                   //  article.Des = item.Groups["des"].Value;
     75                     article.DataType = (int)EHelper.DataType.博客园;
     76 
     77                     if (response.MoArticles.Count > 5) { continue; }
     78                     response.MoArticles.Add(article);
     79                 }
     80 
     81                 response.IsSuccess = true;
     82                 #endregion
     83             }
     84             catch (Exception ex)
     85             {
     86 
     87                 sbLog.AppendFormat("异常信息:{0}
    ", ex.Message);
     88                 response.ErrorCode = (int)EHelper.PmException.获取数据异常;
     89                 response.ErrorMsg = EHelper.PmException.获取数据异常.ToString();
     90             }
     91             finally
     92             {
     93 
     94                 #region 第三方原始信息-日志
     95 
     96                 //PublicClass._WriteLog(sbLog.ToString(), "Cnblogs");
     97 
     98                 #endregion
     99             }
    100             return response;
    101         }
    102         #endregion
    103     }
    View Code

        .注意这里使用 : base("")直接继承了上面说的父类的方法,来初始化配置信息(当然这个可能不算知识点)

        .接下来就是实现的_GetArticles方法里面PublicClass._HttpGet方法封装的HttpClient获取博客园的数据

        .解析了返回的数据信息(这里使用的正则,可能有些同学觉得正则可能还不太熟悉,可以自行百度参考分析下)

        .记录日志,我这里是记录的文本日志暂时注释了,因为怕抓取的信息多,忘记删除占用空间

        以上几点就是实际情况中经常遇到的步奏,这个处理步奏在从来没有对接过第三方接口的人还是值得学习的

        2.HuJiangClass类是抓取了博客园中.Net第一页的数据,步奏和方法和上面相同,请关注代码部分

      1     public class HuJiangClass : BaseClass
      2     {
      3         /// <summary>
      4         /// 初始化xml配置
      5         /// </summary>
      6         public HuJiangClass() : base("") { }
      7 
      8 
      9         #region  获取文章信息 _GetArticles  +BaseResponse
     10 
     11         /// <summary>
     12         /// 获取文章信息
     13         /// </summary>
     14         /// <param name="request"></param>
     15         /// <returns></returns>
     16         public override MoArticlesResponse _GetArticles(object obj)
     17         {
     18             #region 初始化信息
     19 
     20             var request = new MoArticlesRequest();
     21             var response = new MoArticlesResponse();
     22             var sbLog = new StringBuilder(string.Empty);
     23             var url = this.Config.Url;   //这里获取配置文件的url
     24             #endregion
     25 
     26             try
     27             {
     28 
     29                 #region 接口验证数据
     30 
     31                 request = obj as MoArticlesRequest;
     32                 if (request == null)
     33                 {
     34 
     35                     response.ErrorCode = (int)EHelper.PmException.获取数据为空;
     36                     response.ErrorMsg = EHelper.PmException.获取数据为空.ToString();
     37                     return response;
     38                 }
     39 
     40                 #endregion
     41 
     42                 #region 请求数据
     43 
     44                 sbLog.AppendFormat("请求地址:{0}
    ", url);
     45                 var result = PublicClass._HttpGet(url);   //这里一般都是post数据到第三方接口,测试没有第三方可以使用,所以使用抓取博客园首页数据
     46                 sbLog.AppendFormat("返回信息:{0}
    ", result);
     47                 #endregion
     48 
     49                 #region 解析
     50 
     51                 //使用正则解析数据
     52                 var rgs = Regex.Matches(result, "class="titlelnk"\s+href="(?<link>http://www(\w|\.|\/)+\.html)"[^>]+>(?<title>[^<]+)<\/a>[^D]+post_item_summary">(?<des>[^<]+)[^D]+lightblue">(?<author>\w+)<\/a>[^D]+发布于(?<publishtime>\s+(\d|-|\s|:)+)[^<]+");
     53 
     54                 if (rgs.Count <= 0)
     55                 {
     56 
     57                     response.ErrorCode = (int)EHelper.PmException.获取数据为空;
     58                     response.ErrorMsg = EHelper.PmException.获取数据为空.ToString();
     59                     return response;
     60                 }
     61 
     62                 foreach (Match item in rgs)
     63                 {
     64 
     65                     var article = new MoArticle();
     66 
     67                     article.Author = item.Groups["author"].Value;
     68                     article.LinkUrl = item.Groups["link"].Value;
     69                     article.Title = item.Groups["title"].Value;
     70                     article.PublishTime = item.Groups["publishtime"].Value;
     71                    // article.Des = item.Groups["des"].Value;
     72                     article.DataType = (int)EHelper.DataType.博客园NET技术;
     73                     if (response.MoArticles.Count > 5) { continue; }
     74                     response.MoArticles.Add(article);
     75                 }
     76 
     77                 response.IsSuccess = true;
     78                 #endregion
     79             }
     80             catch (Exception ex)
     81             {
     82 
     83                 sbLog.AppendFormat("异常信息:{0}
    ", ex.Message);
     84                 response.ErrorCode = (int)EHelper.PmException.获取数据异常;
     85                 response.ErrorMsg = EHelper.PmException.获取数据异常.ToString();
     86             }
     87             finally
     88             {
     89 
     90                 #region 第三方原始信息-日志
     91 
     92                 //PublicClass._WriteLog(sbLog.ToString(), "Cnblogs");
     93 
     94                 #endregion
     95             }
     96             return response;
     97         }
     98         #endregion
     99 
    100     }
    View Code

      D.今天要讲的主要内容来了,以上算是过度,让人了解对接第三方接口的一些处理步奏和简单的封装吧;这里将看到的是Pm.Api.Controllers空间下BlogsController里面的Post方法,代码如:

      1  // GET api/<controller>
      2         public IEnumerable<string> Get()
      3         {
      4             return new string[] { "欢迎使用-神牛步行3开源框架" };
      5         }
      6 
      7         // POST api/<controller>
      8         public async Task<HttpResponseMessage> Post()
      9         {
     10             HttpResponseMessage response = new HttpResponseMessage();
     11             var baseResponse = new BaseResponse();
     12             try
     13             {
     14                 #region 验证
     15 
     16                 //获取post数据
     17                 HttpContent content = Request.Content;
     18                 var param = await content.ReadAsStringAsync();
     19                 var baseRequest = Newtonsoft.Json.JsonConvert.DeserializeObject<BaseRequest>(param);
     20                 if (string.IsNullOrEmpty(baseRequest.FunName))
     21                 {
     22                     baseResponse.ErrorMsg = EHelper.PmException.请求参数为空.ToString();
     23                     baseResponse.ErrorCode = (int)EHelper.PmException.请求参数为空;
     24                     response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse));
     25                     return response;
     26                 }
     27                 else if (string.IsNullOrEmpty(baseRequest.UserName))
     28                 {
     29 
     30                     baseResponse.ErrorMsg = EHelper.PmException.账号不正确.ToString();
     31                     baseResponse.ErrorCode = (int)EHelper.PmException.账号不正确;
     32                     response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse));
     33                     return response;
     34                 }
     35                 else if (!Enum.IsDefined(typeof(EHelper.DataType), baseRequest.DataType))
     36                 {
     37                     baseResponse.ErrorMsg = EHelper.PmException.参数不合法.ToString();
     38                     baseResponse.ErrorCode = (int)EHelper.PmException.参数不合法;
     39                     response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse));
     40                     return response;
     41                 }
     42                 //验证账号及token
     43 
     44                 #endregion
     45 
     46                 #region 业务
     47 
     48                 var dataTypes = Enum.GetValues(typeof(EHelper.DataType));
     49                 switch (baseRequest.FunName)
     50                 {
     51                     //获取文章集合信息
     52                     case "_GetArticles":
     53 
     54                         //json反序列获取数据
     55                         var r_GetArticles = Newtonsoft.Json.JsonConvert.DeserializeObject<MoArticlesRequest>(param);
     56 
     57                         //初始化任务量
     58                         var tasks = new Task<MoArticlesResponse>[baseRequest.DataType == 0 ? dataTypes.Length : 1];
     59                         var proxy = new Pm_Proxy();
     60                         var j = 0;  //真实任务坐标
     61                         for (int i = 0; i < dataTypes.Length; i++)
     62                         {
     63                             var item = dataTypes.GetValue(i);
     64                             var nType = Convert.ToInt32(item);
     65                             if (nType != baseRequest.DataType && 0 != baseRequest.DataType) { continue; }
     66 
     67                             //使用任务做并行
     68                             var dataType = proxy._DataType(nType);
     69                             var task = Task.Factory.StartNew<MoArticlesResponse>(dataType._GetArticles, r_GetArticles);
     70                             tasks[j] = task;
     71                             j++;
     72                         }
     73                         //30s等待
     74                         Task.WaitAll(tasks, 1000 * 1 * 30);
     75 
     76                         //获取任务执行的结果(整合数据)
     77                         var articles = new MoArticlesResponse();
     78                         foreach (var task in tasks)
     79                         {
     80                             if (!task.IsCompleted) { continue; }
     81                             articles.MoArticles.AddRange(task.Result.MoArticles);
     82                         }
     83 
     84                         articles.IsSuccess = articles.MoArticles.Count > 0;
     85                         baseResponse = articles;
     86                         break;
     87 
     88                     default:
     89                         break;
     90                 }
     91                 response.Content = new StringContent(await JsonConvert.SerializeObjectAsync(baseResponse));
     92                 #endregion
     93             }
     94             catch (Exception ex)
     95             {
     96                 baseResponse.ErrorMsg = EHelper.PmException.获取数据异常.ToString();
     97                 baseResponse.ErrorCode = (int)EHelper.PmException.获取数据异常;
     98             }
     99             return response;
    100         }
    View Code

        1.首先使用了var baseRequest = Newtonsoft.Json.JsonConvert.DeserializeObject<BaseRequest>(param); 来第一次返序列化,得到验证的基本信息如账号,token等

        2.常用的接口形式使用参数节点名称来确定将要执行的方法,或者直接在节点值中标注方法的名称,因此有了这么一句switch (baseRequest.FunName)来判断程序的走向

        3.在此反序列得到真实调用者post给接口的数据(上面第一次反序列使用的是基类,基类里面就包含了验证需要的属性,为什么这里不直接使用第一次反序列的对象呢,因为这里将要传递给后面方法参数的值是子类里面封装的)

        4.开始定义Task任务的数量,一般根据有几个第三方接口第一几个吧,Task<MoArticlesResponse>[]保证后面产生的任务量

        5.Task.Factory.StartNew<MoArticlesResponse>(dataType._GetArticles, r_GetArticles) 方法来创建任务,这里要说的是dataType._GetArticles是之前上面说的请求第三方接口定义的方法,r_GetArticles这个是需要传递的值是object的,这也是StartNew固定的参数类型;再通过Task<MoArticlesResponse>[]存储创建的任务;

        6.Task.WaitAll(tasks, 1000 * 1 * 30);这个WaitAll是自带的,意思是等待任务执行多少毫秒,也算是知识点吧,第一个参数是任务数组,是数组的形式,第二个是毫秒单位的等待时间

        7.最后通过foreach (var task in tasks) 来循环整合task.Result返回的结果

     之后是效果截图DataType表示不同的数据来源:

     

      大致就是这些吧,不知道说的东西是否说明白了,这就是处理对接第三方不同接口的业务逻辑,也是使用task来并行处理的方法;如果有朋友觉得可能说的不太好或者有什么错误地方,还请直接发博客信息,谢谢;

      源码地址:https://github.com/shenniubuxing3/Niu.Pm

  • 相关阅读:
    【算法笔记】多线程斐波那契数列
    RAID技术详解
    Mysql 语句汇总(性能篇)
    JS 网页打印解决方案
    MyEclipse修改
    几个需要学习的点和技术
    MyEclipse配色字体等配置的解决方案
    使用hibernate 分表做增删改查
    Web平台开发流程以及规范
    easyui使用总结
  • 原文地址:https://www.cnblogs.com/wangrudong003/p/5413957.html
Copyright © 2011-2022 走看看