异步使用 HTTP

728 篇文章 1 订阅
86 篇文章 0 订阅

异步使用 HTTP

 

到现在,我们所关注的还只是通过 HTTP 一次检索一个文档,或进行一次更新,在这种情况下,使用异步编程模式就没什么意义了。然而,我们通常的想法是一次能进行多个 HTTP 请求,这样,就可以从多个源检索、汇总数据,在这种情况下,使用 F# 的异步工作流,我们第一次碰到是在第十章“异步编程”一节,就能极大提高应用程序的性能。事实上,我们可能还希望能得到比使用本地磁盘更高的性能,正如我们在第十章所看到的那样,网络的输入输出要比本地磁盘慢很多,这是因为异步编程模式能够在等待输入输出时不阻塞线程,这样,这个线程就可以继续做其他工作;本质上,它是能够以并行的方式等待多个输入输出完成的。由于可以并行化等待时间,就能够期望从高延迟服务(high latency services)中得到最大限度的速度提高。

假设我们想从流行的社交网,推特(twitter)上检查出所有的朋友,再检查出朋友的所有朋友,我们想用这个网络进行一些分析,尝试在朋友的朋友中间找出我们想要找的人。看看清单11-6 我们是如何做的。

 

清单11-6异步使用 HTTP

 

open System

open System.IO

open System.Net

open System.Text

open System.Xml

open System.Xml.XPath

open System.Globalization

open Microsoft.FSharp.Control.WebExtensions

 

// a record to hold details about a tweeter

type Tweeter =

  {Id: int;

   Name:string;

   ScreenName:string;

   PictureUrl:string; }

 

// turn the xml stream into a stronglytyped list of tweeters

let treatTweeter name (stream: Stream) =

  printfn"Processing: %s" name

  letxdoc = new XPathDocument(stream)

  letnav = xdoc.CreateNavigator()

  letxpath = nav.Compile("users/user")

  letiter = nav.Select(xpath)

  letitems =

    [for x in iter ->

     let x = x :?> XPathNavigator

     let getValue (nodename: string) =

       let node = x.SelectSingleNode(nodename)

       node.Value

     { Id = Int32.Parse(getValue "id");

      Name = getValue "name";

      ScreenName = getValue "screen_name";

      PictureUrl = getValue "profile_image_url"; } ]

  name,items

 

// function to make the urls of

let friendsUrl = Printf.sprintf"http://twitter.com/statuses/friends/%s.xml"

 

// asynchronously get the friends of thetweeter

let getTweetters name =

  async{ do printfn "Starting request for: %s" name

       let req = WebRequest.Create(friendsUrl name)

       use! resp = req.AsyncGetResponse()

       use stream = resp.GetResponseStream()

       return treatTweeter name stream }

 

// from a single twitter username get allthe friends of friends of that user

let getAllFriendsOfFriends name =

  //run the first user synchronously

  letname, friends = Async.RunSynchronously (getTweetters name)

  //only take the first 99 users since we're only allowed 100

  //requests an hour from the twitter servers

  letlength = min 99 (Seq.length friends)

  //get the screen name of all the twitter friends

  letfriendsScreenName =

    Seq.takelength (Seq.map (fun { ScreenName = sn } -> sn) friends)

  //create asynchronous workflows to get friends of friends

  letfriendsOfFriendsWorkflows =

    Seq.map(fun sn -> getTweetters sn) friendsScreenName

  //run this in parallel

  letfof = Async.RunSynchronously (Async.Parallel friendsOfFriendsWorkflows)

  //return the friend list and the friend of friend list

  friendsScreenName,fof

 

可以看到,异步的部分与对应的同步部分不太一样,代码需要括在 async { ... } 工作流中,并且,当进行异步调用时,需要使用感叹号(!);除此之外,代码没有变化:

 

let getTweetters name =

  async{ let req = WebRequest.Create(friendsUrl name)

        use! resp = req.AsyncGetResponse()

       use stream = resp.GetResponseStream()

       return treatTweeter name stream }

 

当我们得到了原始中朋友列表后,还要执行一个工作流,因为我们需要等待这个工作流的结果,所以要同步地执行这个工作流。使用 Async.Run 函数执行工作流并等待其结果:

 

let name, friends = Async.Run (getTweettersname)

 

另一个要点是如何并行地调用。这个很简单:获得原始朋友列表后,就可以创建异步工作流列表执行了:

 

// create asynchronous workflows to getfriends of friends

let friendsOfFriendsWorkflows =

  Seq.map(fun sn -> getTweetters sn) friendsScreenName

 

有了这个列表后,能够使用 Async.Parallel 并行执行工作流,用 Async.Run 得到结果:

 

// run this in parallel

let fof = Async.Run (Async.ParallelfriendsOfFriendsWorkflows)

 

并行执行所有这些请求,极大地提高了性能,因为服务器可能并行地处理许多请求,就能够减少等待响应的时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值