基礎(chǔ)11-分布式應(yīng)用程序_第1頁
基礎(chǔ)11-分布式應(yīng)用程序_第2頁
基礎(chǔ)11-分布式應(yīng)用程序_第3頁
基礎(chǔ)11-分布式應(yīng)用程序_第4頁
基礎(chǔ)11-分布式應(yīng)用程序_第5頁
已閱讀5頁,還剩31頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

使用網(wǎng)絡(luò)的應(yīng)用程序,稱為分布式應(yīng)用程序(distributedapplications,現(xiàn)在,已經(jīng)越來越重要。幸運(yùn)的是,.NETBCL和其他的庫提供了許多結(jié)構(gòu),使得通過網(wǎng)絡(luò)通信變得容易,進(jìn)而,用F#創(chuàng)建分布式應(yīng)用程序也是很簡單。已有幾類分布式應(yīng)用程序,通常分為:客戶端-服務(wù)器(cliet-rer)應(yīng)用程序,其中客戶端向服務(wù)器的請求;點(diǎn)對點(diǎn)應(yīng)(r-r)用程序,薩滿教這里,計(jì)算機(jī)之間相互交換數(shù)據(jù)。這一章,重點(diǎn)學(xué)習(xí)客戶端-服務(wù)器應(yīng)用程序,由于這是目前更常見的。不管你想創(chuàng)建哪種分布式應(yīng)用程序,計(jì)算機(jī)之間的數(shù)據(jù)交換都是由協(xié)議控制的。協(xié)議(Scalabilitytolerance:不要因?yàn)榫W(wǎng)絡(luò)故障而使應(yīng)用程序;還應(yīng)該考慮數(shù)據(jù)的一致性(即,能夠保證所有必須的,使用事務(wù)供本地?cái)?shù)據(jù)的能力,當(dāng)網(wǎng)絡(luò)以后可用時(shí),再響應(yīng)網(wǎng)絡(luò)請求。是大多數(shù)的電子郵件的客戶(Security果應(yīng)用程序公開到互聯(lián)崗上就會(huì)有數(shù)千甚至上百萬的潛在者通常需要考慮的包括,,有些是用于客戶端服務(wù)器應(yīng)用程序,而有些適用于點(diǎn)對點(diǎn)應(yīng)用程序。在這一章學(xué)習(xí),(sockets:HTTP/HTTPSFoundationWCF這些協(xié)議是彼此相接的,TCP/IP是最低層的編程接口(API,Windows通信基礎(chǔ)是最的編程接口,服務(wù)是獨(dú)立于Windows通信基礎(chǔ)的,但是,由于Windows通信基礎(chǔ)也實(shí)現(xiàn)了協(xié)議,因此,可以認(rèn)為網(wǎng)絡(luò)服務(wù)是Windows通信基礎(chǔ)的子集。圖11-1是這些11-1.NET子表格中的數(shù)據(jù),在()上找出所有的朋友,以及創(chuàng)建服務(wù)和Windows通,在網(wǎng)絡(luò)上提供用戶界面的簡單方法是開發(fā)應(yīng)用程序應(yīng)用程序不在這里討論,可以,TCPIP套接字TCP/IP套接字提供了跨網(wǎng)絡(luò)的低層控制。TCP/IP套接字是兩臺(tái)計(jì)算機(jī)之間的邏輯連接,通類TCP/IPSystem.Net11-1進(jìn)行了匯總。表11-1使用TCP/IP類服務(wù)器用這個(gè)類入站請求這個(gè)類用于包裝NetworkStream類,用來文本StreamReaderReadLineReadToEnd,以字符StreamReader[StreamWriter創(chuàng)建時(shí),System.Text.Encoding類的NetworkStreamStreamWriter提供兩個(gè)方法Write和Wri 在StreamWriter這一章的第一個(gè)例子,創(chuàng)建一個(gè)聊天程序,有聊天服務(wù)器(見11-1)和客戶(見11-2。聊天服務(wù)器的任務(wù)是等待并客戶端的連接,一旦有客戶端連接,必11-1聊天服務(wù)器openSystemopenSystem.IOopenSystem.Textopenopen//EnhancetheTcpListenerclasssoitcanhandleasyncconnectionstypeSystem.Net.Sockets.TcpListenerwith//TypethatdefinesprotocolforinteractingwiththeClientTable mands=|Addof(string*|Removeof|SendMessageof|ClientExistsof(string*//Aclassthatwillstorealistofnamesofconnectedclientsalong//streamsthatallowtheclienttobewrittentootypeClientTable()=//createthemailletmailbox=MailboxProcessor.Start(funinbox-//mainloopthatwillreadmessagesandupdate//clientname/streamwriterletrecloop(nameMap:Map<string,StreamWriter>)=async{let!msg=inbox.Receive()matchmsg|Add(name,sw)-return!loop(Map.addnamesw|Removename-return!loop(Map.removename|ClientExists(name,rc)-return!loopnameMap|SendMessagemsg-for(_,sw)inMap.toSeqnameMapdo inemsgwith_->return!loopnameMap//startthemainloopwithanemptymaploopMap.empty)///addanewmemberx.Add(name,sw)=mailbox.Post(Add(name,///removeanexistingmemberx.Remove(name)=mailbox.Post(Remove///handlestheprocessofsendingamessagetoallmemberx.SendMessage(msg)=mailbox.Post(SendMessage///checksifaclientnameismemberx.ClientExists(name)=mailbox.PostAndReply(funrc->ClientExists(name,///performasyncreadonanetworkstreampassinga///functiontohandletheletrecasyncReadTextAndCont(stream:NetworkStream)cont//unfortunatlyweneedtospecificanumberofbytesto//thisleadstoanymessageslongerthan512beingbroken//differentasync{letbuffer=Array.create512let!read=stream.AsyncRead(buffer,0,returncontstreamallText}//classthatwillhandleclientconnectionstypeServer()=//clienttabletoholdall ingclientdetailsletclients=newClientTable()//handleseachlethandleClient(connection:TcpClient)//getthestreamusedtoreadandwritefromtheclientletstream=connection.GetStream()//createastreamwritetomoreeasilywritetotheclientletsw=newStreamWriter(stream)//handlesreadingthenamethenstartsthemainloopthat//letrecrequestAndReadName(stream:NetworkStream)(name:string)//readtheletname=name.Replace(Environment.NewLine,//mainloopthathandlesletrecmainLoop(stream:NetworkStream)(msg:string)=//sendreceivedmessagetoallletmsg=Printf.sprintf"%s:%s"namemsgclients.SendMessagemsgwith_-//anyerrorreadingamessagecausesclienttodisconnectclients.Removenameifclients.ClientExists(name)then//ifnameexistsprinterrorandrelaunchrequest ine("ERROR-Nameinusealready!")//nameisgoodlanchthemainloopclients.Add(name,sw) ethenewclientbyprinting"Whatisyouname?" ine("Whatisyourname?");//startthemainloopthathandlesreadingfromthe//createatcplistenerto ing//mainloopthathandlesallnewconnectionsletrechandleConnections()=//startthelisterner//iftherearependingconnections,handleprintfn"NewConnection"http://useathreadpoolthreadtohandlethenewrequestThreadPool.QueueUserWorkItem(fun_->handleClientconnection)|>//return!handleConnections()//nopendingconnections,justloopasync{return!handleConnections()///allow//starttheserverclass(newServer()).Start()我們從頭開始看一下11-1的程序。第一步定義一個(gè)類ClientTable,來管理連接到服務(wù)信箱處理程序把非常客戶端的消息進(jìn)行排隊(duì),通過調(diào)用MailboxProcessor類的Receive方let!msg=Remove、Current、SendMessage和ClientExists: mands|Addof(string*|Removeof|SendMessageof|ClientExistsof(string*loop,loop有一個(gè)參數(shù),它是F#中不可變的Map類,負(fù)責(zé)在用字符串表示的客戶端名字與用StreamWriter表示的到這個(gè)客戶端的連接之間的映射:letrecloop(nameMap:Map<string,StreamWriter>)給下一次循環(huán)。AddRemove的操作實(shí)現(xiàn)很簡單,創(chuàng)建一個(gè)更新過的新映射,然后,傳遞給下一次循環(huán)。下面的代碼只展示了Add操作,因?yàn)镽emove操作非常相似:|Add(name,sw)-return!loop(Map.addnameswClientExists操作有更有趣一點(diǎn),因?yàn)楸仨氁祷匾粋€(gè)結(jié)果,對此,使用(異步應(yīng)答通道ClientExists|ClientExists(name,rc)-return!loopnameMapMailboxProcessorPostAndReply方法,注意,不是前面看到過的Post方法,應(yīng)答通道被加載到聯(lián)合的情況中:mailbox.PostAndReply(funrc->ClientExists(name,可能最有趣的操作是SendMessage,需要枚舉出所有的客戶端,然后把消息傳遞給它們。執(zhí)行這個(gè)在MailboxProcessor類當(dāng)中,因?yàn)檫@個(gè)類實(shí)現(xiàn)了一個(gè)排隊(duì)系統(tǒng),這樣,就能保證|SendMessagemsg-for(_,sw)inMap.to_seqnameMapdo inemsgwith_->,下面看到代碼中最的部分:如何有效地從連接的客戶端讀消息。要有效地讀消為客戶端發(fā)送消息相對并不頻繁。F#通過使用異步工作流,定代碼已經(jīng)很容易了;然而,F(xiàn)#的異步工作流運(yùn)行在最好的狀態(tài),還必須有大量的操作能夠并發(fā)執(zhí)行。在這里,我們想重復(fù)地執(zhí)行一個(gè)異步操作,這是可能的,但有點(diǎn)復(fù)雜,因?yàn)轫毷褂眠B續(xù)的進(jìn)行passing絡(luò)流,并這個(gè)結(jié)果字符串和原始的網(wǎng)絡(luò)流傳遞給它的連續(xù)函數(shù)(continuationfunction。這里的連續(xù)函數(shù)是cont:,///performasyncreadonanetworkstreampassinga///functiontohandletheletrecasyncReadTextAndCont(stream:NetworkStream)cont=async{letbuffer=Array.create5120uylet!read=stream.AsyncRead(buffer,0,letallText=acc+Encoding.UTF8.GetString(buffer,0,read)returncontstreamallText}重新啟動(dòng),結(jié)果會(huì)傳遞給cont函數(shù)。letrecmainLoop(stream:NetworkStream)(msg:string)=//sendreceivedmessagetoallletmsg=Printf.sprintf"%s:%s"namemsgclients.SendMessagemsgwith_-//anyerrorreadingamessagecausesclienttodisconnectclients.Removename把接收到的msg字符串作為消息,執(zhí)行發(fā)送操作;然后,使用asyncReadTextAndCont函數(shù)進(jìn)行遞歸循環(huán),把mainLoop函數(shù)作為一個(gè)參數(shù)傳遞給它,再使用Async.Start函數(shù)發(fā)送fire-and-forget(啟動(dòng)后就不管了)模式啟動(dòng)異步工作流,就是說,它不會(huì)阻塞,接著,創(chuàng)建TcpListener類的實(shí)例。這個(gè)類是完成入站連接工作的,通常用被服務(wù)器的IP地址和端來初始化;當(dāng)啟動(dòng)器時(shí),告訴它的IP地址。通常,它和這臺(tái)計(jì)算機(jī)上網(wǎng)卡相關(guān)的所有IP告訴TcpListener類IPAddress.Loopback,表示只選取本地計(jì)算機(jī)的請求。使用端是判斷網(wǎng)絡(luò)通信只為這個(gè)應(yīng)用程序服務(wù),而不是別的。TcpListener類一次只允許一個(gè)器一個(gè)端口。端的選擇有點(diǎn)隨意性,但要大于1023,因?yàn)槎?到1023是保留給專門的應(yīng)用程序的。因此,我們在最后定義的函數(shù)handleConnections中,使用TcpListener實(shí)例創(chuàng)建的器端口4242:letlistener=newTcpListener(IPAddress.Loopback,4242)server]這個(gè)函數(shù)是個(gè)無限循環(huán),它新的客戶端連接,并創(chuàng)建新的線程來管理。看下面的代碼,printfn"NewConnection"http://useathreadpoolthreadtohandlethenewThreadPool.QueueUserWorkItem(fun_->handleClientconnection)|>得多。11-2是客戶端的完整代碼,注意,要Systems.Windows.Forms.dll才能編11-2聊天客戶端open openSystem.IOopenSystem.Threadingopenletform//createtheletform=newForm(Text="F#Talk//textboxtoshowthemessagesreceivedletoutput=ReadOnly=true,Multiline=//textboxtoallowtheusertosendletinput=newTextBox(Dock=DockStyle.Bottom,Multiline=true)//createanewtcpclienttohandlethenetworkconnectionslettc=newTcpClient()//loopthathandlesreadingfromthetcpclientletload()=letrun()letsr=newStreamReader(tc.GetStream())while(true)dolettext=iftext<>null&&text<>""http://weneedtoinvokebacktothe"gui//tobeabletosafelyinteractwiththecontrolsform.Invoke(newMethodInvoker(fun()->output.SelectionStart<-output.Text.Length))|>//createanewthreadtorunthislett=newThread(newThreadStart(run))//starttheloopthathandlesreadingfromthetcp//whentheformhasloadedform.Load.Add(fun_->load())letsw=new//handlesthekeyupevent-iftheuserhasentereda//oftextthensendthemessagetotheserverletkeyUp()=if(input.Lines.Length>1)thenlettext=input.Textif(text<>null&&text<>"")then witherr-MessageBox.Show(sprintf"Servererror\n\n%O"|>//wireupthekeyupeventhandlerinput.KeyUp.Add(fun_->keyUp//whentheformclosesit'snecessarytoexplicitlyexitthe//asthereareotherthreadsrunninginthebackgroundform.Closing.Add(fun_->//returntheformtothetoplevel//showtheformandstarttheappseventloopdo11-211-2現(xiàn)在我們就來看一下11-2中的客戶端是如何工作的代碼的第一部分完成窗體各部分的初始化,這不是我們現(xiàn)在感的,有關(guān)Windows窗體程序工作原理的詳細(xì)內(nèi)容可以回頭看第八章。11-2中與TCP/IP套接字編程相關(guān)的第一部分是連接服務(wù)器,通過創(chuàng)建TcpClient類的實(shí)例,然后調(diào)用它的Connect方法:lettc=new在這里,我們指定localhost,即表示本地計(jì)算機(jī),端口4242,與服務(wù)器的端口相同。在更實(shí)際的例子中,可以用服務(wù)器的DNS名稱,或者由讓用戶指定DNS名。因?yàn)槲覀冊谕慌_(tái)計(jì)算機(jī)上運(yùn)行這個(gè)示例程序,localhost也是不錯(cuò)的選擇。Load函數(shù)負(fù)責(zé)從服務(wù)器數(shù)據(jù),把它附加到窗體的Load事件,是為了保證窗體裝載并form.Load.Add(fun_->load())temp]義函數(shù)run,然后,使用它啟動(dòng)一個(gè)新線程:lett=newThread(newThreadStart(run))在run的定義中,先創(chuàng)建StreamReader,從連接中讀文本;然后,使用無限循環(huán),這樣,保證線程不退出,能夠連續(xù)從連接中讀數(shù)據(jù)。發(fā)現(xiàn)數(shù)據(jù)之后,要用窗體的Invoke方法更新output.SelectionStart<-output.Text.Length))[temp]keyUp函數(shù)中完成的,它被附加到輸入文本框(input)KeyUp事件,這樣,在文本框中每按一次鍵,下面的代input.KeyUp.Add(fun_->keyUp()keyUp函數(shù)的實(shí)現(xiàn)是非常簡單,發(fā)現(xiàn)超過一行,表示已按過Enter鍵,就通過網(wǎng)絡(luò)發(fā)送所11-1和11-2中,在每次網(wǎng)絡(luò)操作后都調(diào)用了Flush();否則,要等到流的緩存已滿,換(contextswitching,參見第十章“線程、內(nèi)存、鎖定和阻塞”一節(jié))的數(shù)量也會(huì)增加,使用但是,出于某些原因,也可能想用或程序中發(fā)出web請求,例如,通過RSS或Atomfeeds收集站點(diǎn)內(nèi)容。生成HTTP請求,需要用System.Net.WebRequest類的靜態(tài)方法Create。它創(chuàng)建WebRequest對象,表示對統(tǒng)一資源(URL,用于唯一標(biāo)識網(wǎng)絡(luò)上資源的地址)的請求,URL被傳遞給Create方法;然后,使用GetResponse方法獲得服務(wù)器對這個(gè)請求的響應(yīng),用System.Net.WebResponse類表示。下面的例子(11-3)演示了調(diào)用BBC站點(diǎn)上的RSS。這個(gè)例子的是函數(shù)getUrlAsXml,它檢索來自URL的數(shù)據(jù),并把數(shù)據(jù)加載到Xml;其余部分是對數(shù)據(jù)11-3.使用HTTP///makesahttprequesttothegivenurlletgetUrlAsXml(url:string)=letrequest=WebRequest.Create(url)letresponse=request.GetResponse()letstream=response.GetResponseStream()letxml=newXml()///theurlweinterestedleturl="http:///mainapplicationfunctionletmain()=//readtherssfeadletxml=getUrlAsXml//writeoutthetilesofallthenewsletnodes=foriin0..(nodes.Count-1)printf"%i.%s\r\n"(i+1)//readthenumbertheuserwantsfromtheconsoleletitem=int(Console.ReadLine())//findthenewurlletnewUrl=letxpath=sprintf"/rss/channel/item[%i]/link"letnode=xml.SelectSingleNode(xpath)//starttheurlusingthes,thisautomaticall//thedefaultletprocStart=newProcessStartInfo(UseSExecuteFileName=letproc=newProcess(StartInfo=procStart)proc.Start()|>ignore1、需要System.Xml.dll2、url要改成:leturl="]Five-stepcheckfornanoNeanderthalDNAsecretsStemcells'treatmuscleWorldCupsitethreattoCluestopandemicbirdfluMicestarasOlympicfoodClimatebillsetscarbonPhysicspromiseswirelessHeart'cancarryoutownAverageEuropean'isContactlostwithMarsAirguitarT-shirtrocksforChocolate'cutsbloodclotCasefortrawlbanUNchiefissuesclimateJapanesebeginannualwhaleRomanshipthrillsStudyhopefulforworld's使用HTTP谷歌的電子數(shù)HTTPXML公開數(shù)據(jù)的簡單和平臺(tái)無關(guān)性,因此,成為了互聯(lián)網(wǎng)上公開數(shù)據(jù)最流行的方法之一,只需要使用HTTP和部分XML處理,就可以數(shù)量大得驚人的數(shù)據(jù)。谷歌自己公布的電子數(shù)據(jù)就是一個(gè)很好的應(yīng)用在11-4中我們可以看到如何Store。11-4使用HTTP谷歌的電子數(shù)據(jù)openSystemopenSystem.IOopenSystem.Netopenopen//somenamespaceinformationfortheXMLletnamespaces=["at",""openSearch", /-"gsx", //readtheXMLandprocessitintoamatrixofletquerySpreadSheet(xdoc:Xml)xpathcolumnNames=letnav=xdoc.CreateNavigator()letmngr=newXmlNamespaceManager(newdoList.iter(fun(prefix,url)->mngr.AddNamespace(prefix,url))namespacesletxpath= letiter=nav.Select(xpath)seq{forxiniter->letx=x:?>XPathNavigatorletgetValuenodename=letnode=x.SelectSingleNode(nodename,mngr)//readthespreadsheetfromitswebletgetSpreadSheet(url:string)columnNames=letreq=WebRequest.Create(url)useresp=usestream=resp.GetResponseStream()letxdoc=newXml()querySpreadSheetxdoc"/at:feed/at:entry"http://alocationtoholdtheinformationwe'reinterestedintypeLocation={Country:NameValuesList:seq<string*option<float>>//createsalocationfromtherownamesletcrea ocationnamesrow=letcountry=Seq.headrowletrow=Seq.skip1rowlettryParsesletsuccess,res=Double.TryParsesifsuccessthenSomereselseNoneletvalues=Seq.maptryParse{Country=NameValuesList=Seq.zipnamesvalues//getthedataandprocessitintorecordsletgetDataAndProcessurlcolNames=//getthenamesofthecolumnsweletcols=Seq.mapfst//gettheletdata=getSpreadSheeturl//getthereadablenamesoftheletnames=Seq.skip1(Seq.mapsnd//createstronglytypedrecordsfromthedataSeq.map(crea ocationnames)data//functiontocreateaspreadsheetsURLfromit'sletmakeUrl=Printf.sprintf"/feeds/letmain()//thekeyofthespreadsheetwe'reinterestedinletsheatKey="phNtm3LmDZEP61UU2eSN1YA"http://listofcolumnnameswe'reinterestedinletcols=["gsx:location","Hospitalbedsper1000"; "NursingandMidwiferynelper1000"];//gettheletdata=getDataAndProcess(makeUrlsheatKey)//printtheSeq.iter(printfn"%A")datadomain(){Country="Sweden";NameValuesList=[("Hospitalbedsper1000",("NursingandMidwiferynelper1000",Some{Country="Switzerland";NameValuesList=[("Hospitalbedsper1000",Some("Nursingand nelper1000",Some們發(fā)面有幾行代碼與生成HTTP請求,檢索XML文檔的代碼相同:letreq=WebRequest.Create(url)useresp=req.GetResponse()usestream=resp.GetResponseStream()letxdoc=newXml()XML使用HTTP我們已經(jīng)看過使用HTTP協(xié)議檢索數(shù)據(jù)的兩種不同方法下面學(xué)習(xí)如何使用上HTTP進(jìn)行更新。為了能夠讓客戶端發(fā)送數(shù)據(jù),HTTPPOST動(dòng)詞,它把客戶端發(fā)送數(shù)據(jù)作為HTTP請求體的一部分。下一步是使用流行的社交網(wǎng)絡(luò)工具進(jìn)行狀態(tài)更新,地址 (見11-11-5使用HTTPPOST更新狀態(tài)openSystemopenSystem.NetopenletpostTweetusernamepasswordtweet//createatokentolet(token:string)=username+":"+letuser=//determinewhatwewanttouploadasaletbytes=Encoding.ASCII.GetBytes("status="+//connectwiththeupdatepageletrequest= Method="POST",ContentLength=:?>request.ServicePoint.Expect100Continue<-//settheauthorisationrequest.Headers.Add("Authorization","Basic"+//setuptheusereqStream=request.GetRequestStream()reqStream.Write(bytes,0,bytes.Length)postTweet"you""xxx""TesttweetfromF#HTTPPOSTWebRequest時(shí),MethodContentLengthContentType;要想真正讓用戶能夠更新他的狀態(tài),還需要在HTTP頭中加上認(rèn)證的令牌(authenticationtoken://settheauthorisationrequest.Headers.Add("Authorization","Basic"+usereqStream=request.GetRequestStream()reqStream.Write(bytes,0,bytes.Length)可能這個(gè)示例最有意義的地方在于它能運(yùn)行在F#交互中。如果你是一個(gè)粉,可以在不離開編程環(huán)境的情況下,更新你的狀態(tài)。到現(xiàn)在,我們所關(guān)注的還只是通過HTTP一次檢索一個(gè)文檔,或進(jìn)行一次更新,在這種情HTTPF的異步工作流,我成的。由于可以并行化等待時(shí)間,就能夠期望從高延遲服務(wù)(highlatencyservices)中假設(shè)我們想從流行的社交網(wǎng),()上檢查出所有的朋友,再檢查出朋友的所有朋單11-6我們是如何做的。11-6HTTPopenSystemopenopenSystem.NetopenopenSystem.Xml.XPathopen//arecordtoholddetailsaboutatweetertypeTweeter={Id:Name:ScreenName:PictureUrl:string;//turnthexmlstreamintoastronglytypedlistoftweeterslettreatTweetername(stream:Stream)=printfn"Processing:%s"letxdoc=newXPath(stream)letnav=xdoc.CreateNavigator()letxpath= letiter=nav.Select(xpath)letitems[forxiniter-letx=x:?>letgetValue(nodename:string)letnode=x.SelectSingleNode(nodename){Id=Int32.Parse(getValue"id");Name=getValue"name";ScreenName=getValuePictureUrl=getValue"profile_image_url";}]name,items//functiontomaketheurlsletfriendsUrl=Printf.sprintf //asynchronouslygetthefriendsofthetweeterletgetTweettersname=async{doprintfn"Startingrequestfor:%s"letreq=WebRequest.Create(friendsUrlname)use!resp=req.AsyncGetResponse()usestream=resp.GetResponseStream()returntreatTweeternamestream}//fromasingleusernamegetallthefriendsoffriendsofthatuserletgetAllFriendsOfFriendsname=//runthefirstuserletname,friends=Async.RunSynchronously(getTweetters//onlytakethefirst99userssincewe'reonlyallowed//requestsanhourfromtheserversletlength=min99(Seq.lengthfriends)//getthescreennameofallthefriendsletfriendsScreenName=Seq.takelength(Seq.map(fun{ScreenName=sn}->sn)//createasynchronousworkflowstogetfriendsoffriendsletfriendsOfFriendsWorkflows=Seq.map(funsn->getTweetterssn)//runthisinletfof=Async.RunSynchronously(Async.Parallel//returnthefriendlistandthefriendoffriendlistfriendsScreenName,fofasync{...}工作流中,(!letgetTweettersnameasync{letreq=WebRequest.Create(friendsUrlname)use!resp=req.AsyncGetResponse()usestream=resp.GetResponseStream()returntreatTweeternamestream}果,所以要同步地執(zhí)行這個(gè)工作流。使用Async.Run函數(shù)執(zhí)行工作流并等待其結(jié)果:letname,friends=Async.Run(getTweetters//createasynchronousworkflowstogetfriendsoffriendsletfriendsOfFriendsWorkflows=Seq.map(funsn->getTweetterssn)Async.ParallelAsync.Run//runthisinletfof=Async.Run(Async.Parallel創(chuàng)建服務(wù)(WebSOAP換數(shù)據(jù)。服務(wù)由方法(webmethods)組成,公開這些方法,可以在網(wǎng)絡(luò)上運(yùn)行;某種程度上,可以把它看作是F#的函數(shù),因?yàn)榉椒ㄓ忻?,有參?shù),也返回結(jié)果,參代碼。服務(wù)器以URL的形式接收對文件的請求,就必須告訴服務(wù)器,這個(gè)請求應(yīng)該映射到哪一個(gè).NET類。通常用.asmx文件運(yùn)行特定的F#類,響應(yīng)對這個(gè)服務(wù)的請求,如果服務(wù)器獲得對這個(gè).asmx文件的請求。準(zhǔn)確的實(shí)現(xiàn)方法依開發(fā)環(huán)境和托管服務(wù)的服務(wù)器的不同而不同。VisualStudio有內(nèi)置的服務(wù)器,因此,創(chuàng)建新站點(diǎn),只需要簡單地選擇文件->新->,然后,選擇的位置。站點(diǎn)只能運(yùn)行用C#或者VisualBasic.NET寫的頁面,因F#項(xiàng)目,然后,手工修改解決方案文件,以便它駐留在中。聽上去復(fù)雜,做起來很容易,只要把.fsharpp文件到,用記事本打開.sln文件,修改.fsproj文件的路徑。圖11-3是修改前后的.sln文件,左邊是原始文11-3.sln之后,需要配置項(xiàng)目文件的輸出為類庫,并寫到bin子。這似乎要一定的努力,但以后只要按下F5就行了,項(xiàng)目會(huì)自動(dòng)編譯并運(yùn)行。[12、寫到bin ,需要在后期生成事件命令行中輸入這里假定E:\WebSites\ASP.NET是駐留 ,且已經(jīng)新建了bin ]如果沒有VisualStudio,那么下一步要做的事情最好就是用互聯(lián)網(wǎng)信息服務(wù)(InternetInformationServices,IIS)托管站點(diǎn),微軟自己的用于Windows的服務(wù)器。在某種意VisualStudioIIS中托管代碼,需要?jiǎng)?chuàng)建一個(gè)IIS的虛擬,和子bin;然后,.asmx頁面和web.config文件到這個(gè)虛擬中。數(shù)的構(gòu)造函數(shù);也應(yīng)該把它標(biāo)記成System.Web.Service.WebServiceAttribute。如果打算公開(Namespace, 算公開服務(wù)設(shè)置這個(gè)屬性對服務(wù)的可管理性也有好處那么類的成員即方法,用System.Web.Service.WebMethodAttribute標(biāo)記[原文WebServiceAttribute有誤],它也有11–7定義了一個(gè)簡單服務(wù)創(chuàng)建了類型Service,它有一個(gè)成員Addition的服務(wù),11-7創(chuàng)建一個(gè)簡單服務(wù)namespaceStrangelights.WebServicesopenSystem.Web.Services[<WebService(Namespace typeService=inheritWebServicenew()={}[<WebMethod(Description="Perfomsintegeraddition")>]members.Addition((x:int),(y:int))=x+y[x.Additions.Additionsxy相同的]要想讓服務(wù)器發(fā)現(xiàn)服務(wù),需要?jiǎng)?chuàng)建.asmx文件。下面是.asmx文件的示例,最重到如圖11-4所示的界面,可以為服務(wù)提供參數(shù)值,并調(diào)用這個(gè)服務(wù)。圖11-4調(diào)用本地的服46和28作為參數(shù)值,調(diào)用這個(gè)服務(wù),產(chǎn)生下面的<?xmlversion="1.0"encoding="utf-8"<int .NET(serialize)XML;然而,結(jié)果XML在其他編程語言中使用了。要避免這種情況,最好是使用XSD架構(gòu)來定義想要跨服務(wù)傳遞的對象。XSDXMLXML文檔的結(jié)構(gòu),描述的內(nèi)容包括哪雖然自己定義XML架構(gòu)是可能的,但是,VisualStudio有一些創(chuàng)建架構(gòu)的圖形方式和文本要看到的示例使用了RNAML,這個(gè)在上可以找到,這是為想以XML形式交換RNA(核糖核酸,一種由分子生物學(xué)家研究的物質(zhì))信息的人們提供的架構(gòu)。從上架構(gòu)以后,就可以用使用命令行工具xsd.exe把架構(gòu)轉(zhuǎn)換成.NET形式的表示。這個(gè)工具生成以#類來表示XML數(shù)據(jù),通常,XML中的每個(gè)標(biāo)記都會(huì)成為.NET的一個(gè)類;然后,可以把這個(gè)C#文件編譯成一個(gè).NET程序集,供F#或其他任何.NET庫使用。完成令行可能像這樣:xsdrnaml.xsd注意,需要把的架構(gòu)文件rnaml.xml重命名為rnaml.xsd,工具才能正確運(yùn)行下面的示例演示如何創(chuàng)建服務(wù),返回酵母RNA分子結(jié)構(gòu),這是RNAML上系列示 文件,把被請求的文件到.NET類型上,來處理請求:11-8展示了這個(gè)服務(wù),可以看到,這個(gè)的基本組件與前面簡單的服務(wù)是WebService特性;代碼所做的工作是定義11-8創(chuàng)建服務(wù),返回RNA分子的定義namespaceStrangelights.WebServicesopenSystem.Web.Services//thewebserviceclass[<WebService(Namespace typeDnaWebService()=inherit//theweb[<WebMethod(Description="Getsarepresentationofayeastmolecule")>]memberx.GetYeastMolecule()=//thecodethatpopulatestheyeastxmllettax=newtaxonomy(="Eukaryota",kingdom=phylum= ycota",``class``=order="Saccharomycetales",family="Saccharomycetaceae",genus="Saccharomyces",species="Saccharomycesletid=newidentity(name="SaccharomycescerevisiaetRNA-taxonomy=letyeast=newmolecule(id="Yeast-tRNA-Phe",identity=id)letnumRange1=newnumberingrange(start="1",Item="10")letnumRange2=newnumberingrange(start="11",Item="66")letnumSys=newnumberingsystem(id="natural",usedinfile=true)numSys.Items<-[|boxnumRange1;boxnumRange2|]letseqData=newseqData.Value<-"GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA"letseq=newsequence(numberingsystem=[|numSys|],seqdata=seqData)yeast.sequence<-[|seq|]另外,同樣簡單,可以使用基于的測試方法,運(yùn)行前面的代碼,產(chǎn)生如下的<?xmlversion="1.0"encoding="utf-<moleculexmlns:xsi="xmlns:xsd=""id="Yeast-tRNA-<name>SaccharomycescerevisiaetRNA-<>Eukaryota</ <species>Saccharomyces<numbering-systemid="natural"used-in-<numbering-</numbering-<numbering-</numbering-</numbering-<seq-data>GCGGAUUUAGCUCAGUUGGGAGAGCGCCAGACUGAAGAUCUGGAGGUCCUGUGUUCGAUCCACAGAAUUCGCACCA</seq-data>這個(gè)示例返回一個(gè)靜態(tài)的、不變的XML文檔,并沒有什么特別的用處,但是,很容易看出GetYeastMoleculeGetMolecule,C++JavaF#中處理這種數(shù)據(jù)非常簡單。XML網(wǎng)絡(luò)上傳輸?shù)男畔⒁用芑蚝灻?。法是升級到Windows通信基礎(chǔ)(WindowsFoundation,WCF,[但是,由于沒有到rnamk.xml,因此,后面的工作也就沒法進(jìn)行了。]Windows通信基礎(chǔ)(WindowsCommunicationWindows通信框架(WindowsCommunicationFramework,WCF)的目標(biāo)是提供創(chuàng)建分布式然后,以各種不同方法公開這個(gè)服務(wù)。例如,服務(wù)總是傳遞XML消息,但是,可以把WCFXML消息,還能傳遞二進(jìn)制數(shù)據(jù);另外,WFC服務(wù)可以托管在入站消息,而不需要在桌面上沒有安裝服務(wù)器WCF是.NET框架3的一部分,提供了一組編程接口,隨WindowsVista同時(shí),且已經(jīng)安裝在操作系統(tǒng)中;也可以從http:/ 上,安裝在WindowsXP和Windows服務(wù)器2003上(htt 的地址好像不能了;.NET的安裝也越來越復(fù)雜了。WCF使用的協(xié)議是基于一組擴(kuò)展服務(wù)的規(guī)范有時(shí)簡稱為WS-*協(xié)議因?yàn)槊恳粋€(gè)協(xié)議通常都以WS-前綴命名比如,需要從.NET框架3SDK。在第一個(gè)示例(見11-9)中,構(gòu)建一個(gè)簡單的WCF服務(wù),托管在服務(wù)器中,看起來更像一個(gè)簡單的服務(wù);然后,再改進(jìn)這個(gè)服務(wù),以展示W(wǎng)CF的有趣功能。為了創(chuàng)建在服務(wù)器上托管的WCF服務(wù)與“創(chuàng)建服務(wù)一節(jié)中討論的步驟相同,不同的是,不能托管在運(yùn)行Linux的Apache上,因?yàn)閃CF依賴于Windows的一些特定11-9創(chuàng)建簡單的WCF服務(wù)openSystem.ServiceModel//theservicecontract(Namespace typeIGreetingService=Greet:name:string->//theserviceimplementationtypeGreetingService()=interfaceIGreetingServicememberx.Greet(name)="o:"+WCFIGreetingServiceGreet。要WCFSystem.ServiceModel.ServiceContractAttribute標(biāo)記接口,它應(yīng)該包含服務(wù)的命名空間;在服務(wù)接口內(nèi)部,打算公開的每一個(gè)函數(shù)都要用OperationContractAttribute去標(biāo)記。每一個(gè)參數(shù)都有名字[,這一點(diǎn)很重要];在F#中,WCF契約的接口,如果其函WCF框架中,這個(gè)參數(shù)名是用于(通過反射reflection)創(chuàng)建在網(wǎng)絡(luò)上發(fā)送的數(shù)據(jù)。GreetingService類實(shí)現(xiàn)了契約,簡單地提供一個(gè)問候語,把傳遞的名字加到“o為了把服務(wù)集成到服務(wù)器,需要?jiǎng)?chuàng)建.svc文件,它的作用與服務(wù)的.asmx文件相.svc文件;注意,我們在這個(gè)示例中看到的就是完整的文件,這種文件通常只有一行。在.svc文件中最重要的特性是Service,它告訴服務(wù)器應(yīng)該使用哪一種類型:<%@ServiceHostDebug="true"Service="Strangelights.Services.GreetingService"最后,必須配置服務(wù)。因?yàn)閃CF可以選擇協(xié)議,所以要使用配置文件告訴它使用哪一個(gè)。11-10中的配置文件顯示了能夠用來配置服務(wù)的配置文件。Service元素定義兩個(gè)端點(diǎn),這些協(xié)議是客戶端用來和服務(wù)的。其中一個(gè)端點(diǎn)是標(biāo)準(zhǔn)的服務(wù)HTTP綁定,另一如何與服務(wù)。當(dāng)創(chuàng)建客戶端時(shí),將使用這個(gè)端點(diǎn)11-10WCF服務(wù)的配置文件<configurationxmlns="binding="mexHttpBinding"address="mex"/><serviceMetadatahttpGetEnabled="true"一節(jié)討論過的wsdl.exe工具相似。使用SvcUtil.exe創(chuàng)建服務(wù)的,用下面令行,適當(dāng)?shù)馗淖円幌耈RL:svcutil.exe這將生成一個(gè)C#文件,然后,可以把它編譯成一個(gè).NET程序集在F#中使用;它還生成一個(gè).config文件,可以用于配置任何客戶端的應(yīng)用程序。使用就變得非常簡單了,添加了對這個(gè)的.dll文件的以后,只要?jiǎng)?chuàng)建的實(shí)例,就可以用適當(dāng)?shù)膮?shù)調(diào)用它的Greet方法了。11-11展示了的一個(gè)示例;因?yàn)檎{(diào)用的Dispose方法很重要,所以,實(shí)例化時(shí),使用了use綁定[原文忘記修改了,還是第一版的內(nèi)容。]。11-11調(diào)用WCF服務(wù)letmain()=useclient=newGreetingServiceClient()whiletruedoprintfn"%s"Console.ReadLine()|>useclient=newGreetingServiceClient()printfn"%s"(client.Greet("Rob"))Console.ReadLine()|>ignoredomain()11-12是一個(gè)產(chǎn)生的配置文件的示例,已經(jīng)刪除了一些特定的內(nèi)容,以保證示例運(yùn)行11-12調(diào)用WCF服務(wù)的配置文件<bindingname="WSHttpBinding_IGreetingService"closeTimeout="00:01:00"openTimeout="00:01:00"receiveTimeout="00:10:00"sendTimeout="00:01:00"bypassProxyOnLocal="false"transactionFlow="false"textEncoding="utf-8"useDefaultWebProxy="true"allow<readerQuotasmaxDepth="32"enabled="false"/><endpointaddress="運(yùn)行11-11產(chǎn)生的結(jié)果如下o:WCFWCF中,最讓我感到興奮的方面是它可以把服務(wù)托管在任意程序中,而不一定是服務(wù)在fsi.exe。為了使前面的示例能夠運(yùn)行在fsi.exe,還需要對它做一些修改,但修改相當(dāng)簡11-13展示了11-9修改后的版本,它能夠運(yùn)行在fsi.exe。11-13托管在F#交互中的服務(wù)#I@"C:\ProgramFiles\ReferenceAssemblies\\Framework\v3.0";;#r"System.ServiceModel.dll";;openopenopen//arangeofgreetingsthatcouldbeusedletmutablef=(funx->"o:"+x)f<-(funx->"Bonjour:"+f<-(funx->"Goedendag:"+//theservicecontract(Namespace typeIGreetingService=Greet:name:string->//theserviceimplementationtypeGreetingService()=interfaceIGreetingServicememberx

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論