我是這樣寫程序的


背景

      我接到了任務,大體是說財務需要對賬,所以需要Paypal的交易記錄,直接去Paypal的后台去對是很浪費時間的事情.討論下來就是要使用Paypal的Api獲取交易記錄到本地.然后再想辦法使用這些數據,那么現在的任務就是獲取Paypal交易記錄

任務: 獲得Paypal交易記錄存到本地

分析問題:

     老實說問題很簡單明確.需要用到Paypal獲取交易信息的API,好在Paypal提到了.NET的開發SDK,只要配置好相關參數就能使用.通過閱讀SDK發現需要使用2個接口,一個TransactionSearch用來獲取交易碼列表,這是交易的唯一ID,但是這個接口返回的信息有限.需要獲取更詳細的信息需要用另一個接口transactionDetails,這個接口的參數就是交易碼. 總的來說,第一個接口獲取交易碼列表,第二個接口像是從列表點進去獲取詳細信息, 做.NET企業級開發的人想都不用想就知道我在說什么.

面對這個需求,首先想到的關鍵點

        關鍵點1: 交易記錄是不停的隨時產生的,每一條交易記錄都需要調用一次獲取詳細信息的api, 如果有需要的時候就運行一次程序,交易記錄會越來越多. 我不知道哪天短時間內向Paypal提交上萬的請求會不會被屏蔽.所以最好就是程序能一直運行,不停的調用api,免得 一次過多調用被屏蔽.而且, Paypal份額服務器在國外,而且API都是基於http協議,每次調用延時都比較厲害,分散獲取交易信息,避免了哪天要用的時候等上1個2個小時的情況

        關鍵點2: 獲取交易碼列表的API假定每次調用能獲取100個,然后調用100次獲取詳細清晰的API,這一次下來耗時也挺讓人崩潰,明顯使用多線程,每個線程調用一次TransactionDetail,這樣能減少等待時間,加快速度

      有上上面的基調,基本上我確定了第一版的程序是什么樣的了,我做個winform程序,弄個按鈕,程序運行之后,點擊按鈕開始抓取交易信息,然后最小化到系統托盤顯示這個程序還活着一直在執行.程序運行的時候,一個線程會處於循環狀態, 執行獲取列表的的調用,一旦抓到交易碼,就創造多個線程來獲取詳細信息,處理完畢,休眠1個小時,如此循環.

從拿到任務到做出這個設計很明顯是個正確的方向,肯定是能獲得數據的,但是我並沒有開始寫代碼. 我需要花時間的做好詳細的設計,並在設計的過程中希望能發現潛在的問題.

問題來了:

     1. 線程崩潰, 執行transactionDetails的線程, 因為異常崩潰掉, 比如網絡超時等. 如果所有執行transactionDetails的線程都崩潰了,獲取的交易碼列表沒有處理嗎,那程序不就會卡在這里了嗎.

解決辦法:

     為了避免這樣的情況,執行TransactionSearch的線程,執行GetTransactionDetail的線程要同時創建,都要一直執行, 都要變成守護線程這樣的東東.   執行TransactionSearch的線程變成了生產者,執行GetTransactionDetail的線程變成了消費者, 把這個問題變成了一個生產者和消費者問題.

為了解決這個問題的第二版設計設計出來了, 依然是這個winform程序,運行方式也一樣. 程序運行的時候,會創建一個生產者線程調用TransactionSearch創建3個線程調用transactionDetails,生產者獲取的交易碼寫入一個隊列,消費者從隊列中獲取交易碼,調用api獲取詳細信息,寫入數據庫.生產者和消費者之前的同步問題,Google和baidu都能幫忙搞定.

項目結構

     到這里的時候我打算開始寫代碼了,因為暫時我實在想不出有什么問題會讓程序沒法運行. 所以我創建了工程,做好了模塊划分

       Paypalapi的API接口部分划入PaypalHelper類. 這個類就兩個靜態方法,分別是對TransactionSearch,transactionDetails的封裝

      數據庫的訪問部分只有比較簡單的操作,划入DBHelper. 這部分的功能主要是把交易碼和交易記錄細節寫入數據庫,甚至硬編碼也沒什么問題.

     加入一個模型表示交易詳細信息, TransactionDetail,用來在各模塊交換信息

      加入一個Executor類表示執行部分. 用來封裝消費者和生產者線程的創建和同步

      加入一個winform作為主界面,調用Executor

雖然功能不多,但是分層分模塊的想法也要始終貫徹啊,知道了要怎么做,加上詳細的模塊划分,編碼其實就是很機械的動作了.話了點時間編寫調試,終於可以運行了.運行之后交易記錄慢慢的一條一條的存入數據量了,

第二版

        問題:  運行一段時間之后就發現和Paypal后台看到的交易記錄不同,中間會漏掉一些交易記錄,而且剛一個時間段之內的.

        分析問題: 我發現是生產者休眠1個小時帶來的問題,考慮到1個小時的休眠時間是因為我不喜歡程序不停的訪問Paypal的API, 問題在於,如果抓來的數據處理完成的時間超過1個小時,生產者線程還要等待1個小時后之后再抓取數據了,所以中間會漏掉一個時間段的交易記錄了.

        解決辦法: 明顯生產者線程的執行收到消費線程處理速度的影響,那么這個生產者現場就要和消費者線程分離, 那么何必不單純創建一個線程,這個線程就是只執行TransactionSearch,然后把交易碼存入數據庫,然后休眠1小時,而之前的生產者線程從數據里面獲取交易碼,消費者線程處理之后更新數據庫,標識交易碼已處理.總體來說,就是增加一個線程來執行獲取交易碼列表,而生產者和消費者線程的模式基本不變.

      第二版很快就做了出來,,在我的電腦上運行了一整天,檢查數據之后,發現問題解決了. 到了這一步,我又很快的認為,我暫時想不到有什么問題會干擾程序的正確性了,於是我遠程桌面連接上服務器准備運行了,服務器上正常運行沒有任何問題,於是我就退出了,過了2個小時我去檢查數據庫發現沒數據,太奇怪了,於是我登上服務器,發現系統托盤不見了,打開任務管理器看不到進程,我檢查下日志,發現沒有任何異常記錄.於是我很憤怒認為是那個家伙把我的程序關掉了.重新運行之后我就退出了遠程桌面,然后到辦公室里問,是那個家伙不長眼, 結果沒問出來,回來再看程序又退出了!!! 我意識到,可能還是程序的問題,搜索一下發現是遠程桌面的問題,遠程桌面的用戶會話結束后,會終結掉用戶的所有程序.

第三版

    分析問題:一般的桌面應用都有用戶,自己的電腦一直有用戶登錄,所以我怎么測試都沒測不出來的,服務器上運行的程序是沒有用戶的,通過遠程桌面運行的程序,在退出桌面之后就會終結. windows上面不用登陸就能運行的程序是什么?windows服務!!

       現在要做的就是要把我的程序編程一個Windows服務.老實說,我之前根本也沒寫過服務,但是.net下創建一個服務器並不是什么難事. baidu了一下,找了一篇詳細介紹怎么創建和安裝Windows服務的的博客,對着創建一個服務類型的工程,把代碼放在指定的接口里面就成了,事實上,第二版里面程序的入口在winfom下,只需要去掉winform,把代碼轉到服務入口就行. 第三版很快就出來了,服務安裝之后正常運行, 數據終於正常了.

第四版

      新的需求又來了,他們需要定制獲取交易碼的時間. 從第三版的程序可以看到,程序中獲取交易碼的線程是在程序開始運行之后每1個小時獲取一次.事實上Paypal的交易記錄產生有是規律的,半天明顯交易多,晚上交易就少了.

所以最好能制定一個更合理的任務規划來獲取交易碼列表.

      分析問題: 當時我就覺得一陣眩暈,這么詳細的任務調度控制我之前本來沒做過,唯一能想到的就是用計划任務,這很可能就會導致程序分拆成兩個.獲取交易碼列表的功能單獨做個一個程序,然后用計划任務來執行.

      解決辦法: 最先想到的辦法就是用計划任務,帶來的問題就是兩個程序分別運行.一個沒多少功能的程序這樣拆分我覺得是沒什么意義的,於是我上網搜索了下, 目的明確又簡單,就是想找個任務調度之類的什么組件,免得自己開發個,那就要吐血了. 果然,很快我就發現了Quartz.NET, 詳細的閱讀Quartz.NET的介紹和文檔之后發現足夠滿足需求,但是還有點不放心,於是我寫了個Demo,來測試Quertz.NET,測試之后我就決定引入項目了.

       第四版的很快就出來了,想對於第三版.變化就是取消了獲取交易碼列表的線程,把它變成了用Quartz.NET實現的一個任務,能很細粒度的控制獲取交易碼的時間. 而對交易碼的處理的生產者和消費者線程基本不變

      進一步的改進方向

       一是服務是在后面運行的,沒有GUI. 二是程序中生產者和消費者線程的數量是固定的,休眠時間也是固定的. 可以考慮外界一個GUI程序來啟動和停止服務, 並且用來配置生產者和消費者線程的數量及等待時間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM