使用F#中的代理的Twitter流API

时间:2010-08-25 15:37:42

标签: api f# twitter stream agents

来自Don Syme博客(http://blogs.msdn.com/b/dsyme/archive/2010/01/10/async-and-parallel-design-patterns-in-f-reporting-progress-with-events-plus-twitter-sample.aspx)我试图实现一个Twitter流监听器。我的目标是遵循twitter api文档的指导,该文档说“在构建高可靠性系统时,通常应该在处理之前保存或排队推文”。

所以我的代码需要有两个组件:

  • 堆积并处理每个状态/推文json的队列
  • 读取将json字符串中的推文转发到队列的twitter流的内容

我选择以下内容:

  • 我发布每条推文的代理,解码json并将其转储到数据库
  • 一个简单的http webrequest

我还想将插入数据库中的任何错误转储到文本文件中。 (我可能会为所有错误切换到主管代理。)

两个问题:

  • 这是我的策略吗?如果我理解正确,代理就像一个智能队列并异步处理它的消息(如果它的队列中有10个人,它会在一定时间处理它们,而不是等待第一个完成然后第二个等...),对吗?
  • 根据Don Syme的帖子,之前的所有内容都是隔离的,因此StreamWriter和数据库转储是隔离的。但因为我需要这个,我从不关闭我的数据库连接......?

代码类似于:

let dumpToDatabase databaseName = 
   //opens databse connection 
   fun tweet -> inserts tweet in database

type Agent<'T> = MailboxProcessor<'T>



 let agentDump =
            Agent.Start(fun (inbox: MailboxProcessor<string>) ->
               async{
                   use w2 = new StreamWriter(@"\Errors.txt")
                   let dumpError  =fun (error:string) -> w2.WriteLine( error )
                   let dumpTweet =  dumpToDatabase "stream"
                   while true do 
                       let! msg = inbox.Receive()
                       try 
                           let tw = decode msg
                           dumpTweet tw
                       with 
                       | :? MySql.Data.MySqlClient.MySqlException as ex -> 
    dumpError (msg+ex.ToString() ) 
                        | _ as ex -> () 



                             }
                             )

    let filter_url = "http://stream.twitter.com/1/statuses/filter.json"
    let parameters = "track=RT&"
    let stream_url = filter_url

    let stream = twitterStream MyCredentials stream_url parameters


    while true do 
        agentDump.Post(stream.ReadLine())

非常感谢!

使用处理器代理编辑代码:

let dumpToDatabase (tweets:tweet list)= 
    bulk insert of tweets in database    

let agentProcessor = 
        Agent.Start(fun (inbox: MailboxProcessor<string list>) ->
           async{
               while true do 
                       let! msg = inbox.Receive()
                       try
                          msg
                          |> List.map(decode)
                          |> dumpToDatabase 
                        with
                        | _ as ex -> Console.WriteLine("Processor "+ex.ToString()))
                 }
                 )



let agentDump =
        Agent.Start(fun (inbox: MailboxProcessor<string>) ->
                  let rec loop messageList count = async{
                      try
                          let! newMsg = inbox.Receive()
                          let newMsgList = newMsg::messageList
                          if count = 10 then 
                               agentProcessor.Post( newMsgList )
                               return! loop [] 0
                          else                    
                               return! loop newMsgList (count+1)
                      with
                      | _ as ex -> Console.WriteLine("Dump "+ex.ToString())

                  }
                  loop [] 0)

let filter_url = "http://stream.twitter.com/1/statuses/filter.json"
let parameters = "track=RT&"
let stream_url = filter_url

let stream = twitterStream MyCredentials stream_url parameters


while true do 
    agentDump.Post(stream.ReadLine())

1 个答案:

答案 0 :(得分:5)

我认为描述代理的最佳方式是正在运行的进程保持某种状态,并且可以与其他代理(或网页或数据库)进行通信。编写基于代理的应用程序时,通常可以使用多个代理来相互发送消息。

我认为创建一个从Web读取推文并将其存储在数据库中的代理是一个不错的选择(尽管您也可以将推文作为代理的状态保存在内存中)。

  • 我不会一直打开数据库连接 - MSSQL(也可能是MySQL)实现连接池,因此当你释放它时它不会自动关闭连接。这意味着每次需要将数据写入数据库时​​重新打开连接更安全,效率更高。

  • 除非您希望收到大量错误消息,否则我也可能会对文件流执行相同的操作(写入时,您可以打开它,以便将新内容添加到结尾)。< / p>

F#代理队列的工作方式是逐个处理消息(在您的示例中,您正在使用inbox.Receive()等待消息。当队列包含多条消息时,您将获得一条消息一个(循环)。

  • 如果您想一次处理多个消息,您可以编写一个等待10个消息的代理,然后将它们作为列表发送给另一个代理(然后执行批量处理)。 / p>

  • 您还可以为timeout方法指定Receive参数,这样只要它们都在一秒钟内到达,您就可以等待最多10条消息 - 这样,您就可以优雅地实现长时间不保留消息的批量处理。