HTTP客戶端演進之路

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 協議可能是現在 Internet 上使用得最多、最重要的協議了,越來越多應用程序需要直接通過 HTTP 協議來訪問網絡資源。一般的情況下我們都是使用瀏覽器來訪問一個WEB服務器,用來瀏覽頁面查看信息或者提交一些數據、文件上傳下載等等。不通過瀏覽器來訪問服務器的資源呢?一種常見的場景是,通過向另一個http服務器發送請求,獲得數據。最常規的做法是使用同步http請求的方式,即下文展示的同步模式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景1:向服務端URL_STR提交了nixian ,handsome 的賬戶密碼,並且附件了名稱爲LOCALFILE說明文檔。下文以及代碼實現無特別說明參照該場景,模式演進和原理無特別說明以Apache HTTP Client的解決方案作爲參考。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在追求高性能HTTP客戶端的實踐中,我們從同步模式,異步模式演進到極致模式,並開源了極致模式客戶端框架besthttp,在“場景1”的檢測中,其性能10+倍優於同步模式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、表單模式,面向瀏覽器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":""}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、 同步模式,解決網絡交互"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端發送http請求是串行的,也就是第一個請求未完成的情況下,第二個請求發不出去,請求按照提交的順序發送給服務端。引入多線程提高併發性,然而多線程對併發發送http請求的性能提升也是有限的,比如10個線程同時只能發送10個請求,假如每個請求從發送到得到結果的時間是1秒,那麼10個線程每秒鐘也只能發送10個請求,而線程數量收到系統資源的約束,因此多線程並不能很好的解決客戶端高併發發送請求的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使把多線程轉換成線程池的模式,那麼使用同步http時,在高併發場景下,線程池的隊列中會堆積大量請求任務發不出去,而被請求的目標服務器,卻還遠沒有達到瓶頸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/07\/078ee0ae91159a55b94a4aab75ae9064.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,對於發送請求的主體是阻塞的,即使與響應請求無依賴的邏輯也被阻塞。想要讓發送請求與無關聯的邏輯並行,需要開闢新的線程,這與上面的情況類似,線程的控制增加了代碼維護難度和系統不穩定的風險。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、異步模式 ,面向高併發"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/03\/03d5ec45b4bf49538ebd2f881a315059.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"異步模式可以幫助構建高性能的客戶端,數據通過更快和無阻塞方式的發送服務端,逼近服務端處理的能力,系統性能的上限由客戶端轉移至服務端,而服務端通常有更好的擴展性。異步模式提供重要的措施一是無阻塞,另外一個措施是是零拷貝。如何實現無阻塞,目前大部分異步模式的組件使用非阻塞 I\/O 模型。同步模式下的業務線程負責發送->阻塞等待->返回->業務處理,異步模式的I\/O 模型固定的IO 線程負責發送\/接受響應,業務線程負責業務邏輯,清晰的分工意味更高效的配合。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IO線程委託給操作系統調度,成千上萬連接可以同時進行,充分利用系統資源和原本等待返回的時間,而不是空等啥都幹不了。同步模式中請求的發送的邏輯是把數據包按字節順序的發送到網絡和物理線路上,異步模式註冊“寫就緒”事件立即返回。http結果,以“讀就緒”事件的形式觸發回調函數的響應,發送數據和處理結果是兩個不同的分支,處於不同的線程上下文。事件的觸發由操作系統內核同時對多個TCP連接監聽,對已經就緒的寫讀事件經過分發器把讀寫事件源分發給IO線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"發送請求和響應處理處於不同的線程上下文,主體發送完數據後,與響應沒有依賴關係的業務邏輯可以方便地與響應處理並行,省去了新線程的創建,避免了系統資源峯谷變化之間的不穩定。與同步模式下 BIO + 線程池的模型相比,無限的連接的發送\/接受可以被有限的線程處理,不但大量的併發連接得以處理,數據傳輸的效率也更高效。客戶端可以在1 秒內同時將10 個請求發出,在處理其他業務邏輯同時處理響應的結果,如果服務端能順利地同時處理10個請求,整個系統的吞吐率提高了10倍。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/4c\/4c510a4d403fe3e6e7a7cf0d8f7f3aab.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"零拷貝依賴操作系統提供的“更合理的系統調用”,JAVA NIO 支持對通道和緩存的操作,在sun.nio.ch.FileChannelImpl 提供了零拷貝的API 。 使用FileChannel.transferTo() 方法節約了內核空間\/用戶空間的頻繁切換和CPU 拷貝,基於不同的系統內核和基礎組件的實現,節約了兩次以上的 CPU 拷貝。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f4\/f497f5efa6962db4af2e92f1d7adb180.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用異步模式組件Apache HttpAsync Client 時有幾個問題:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"複雜場景功能上的缺失"},{"type":"text","text":":如無法直接的使用FORM 表單提交功能等;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"未對內存有效的控制"},{"type":"text","text":";"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"功能與性能無法兼顧"},{"type":"text","text":":使用高性能org.apache.http.nio.entity.NFileEntity 高效傳輸文件時無法用鍵值對錶示發送內容。org.apache.http.nio.entity.NFileEntity 是FileChannel.transferTo()在HTTP交互場景的擴展,文件內容由硬盤直接傳輸至網卡。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"編程模型複雜"},{"type":"text","text":":異步編程模型爲了獲取方法異步執行的結果,在調用後返回future 作爲程序 “交互界面”,future 抽象類定義了獲取結果,是否完成交互功能等。與future 對應的futurecallback 是對返回的結果正確和異常的處理。多個future 存在依賴關係時,需要級聯回調與之對應的futurecallback。HttpAsyncClient 使用BasicFuture 作爲future 與 futurecallback 的銜接,每兩個 future 的依賴需要三個對象來維繫,具體場景會表現得更加複雜。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、極致模式,最懂操作系統的HTTP 客戶端"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/df\/dfac39606ade389d078d05a734f6aa52.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.1 極致的資源管理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"極致模式提倡將連接,線程,內存等資源池化管理,減少系統運行時創建資源帶來的性能損耗,降低開發員管理資源的風險。作爲最親近操作系統的HTTP 客戶端框架模式,可以在內存,傳輸路徑選擇適合不同場景的最佳組合,追求極致的性能表現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"極致模式繼承了異步模式的所有優點,同時對提及的問題做了極致優化。在異步模式基礎上,使用內存池加強對內存的緩存和回收,同時支持對堆內內存,直接內存以及自定義內存的託管。HTTP交互有豐富且複雜的場景,不同場景之下選擇不同的內存控制方案結合“拷貝”最少路徑進一步提高場景方案的數據傳輸效率。內存自定義比較普遍存在於接受HTTP 響應時,無法預先知曉報文的大小,既要對內存緩存,又要支持內存的自動擴張以容納不可預料的內容,在內容使用後回收。另外一個優勢是,將內存資源和連接資源進行解耦,報文數據被託管緩存後就立即釋放連接,避免不及時消費導致內存和連接相互糾纏而泄露。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論同步模式還是異步模式,Apache HTTP 解決方案體系都提供了對連接池的支持。連接池有助於提高網絡資源的使用效率,但也引來一個極具挑戰性的問題,即可能從池中獲取一個不可用的連接。相對自身可控的關閉,服務端的單方面的關閉和網絡異常顯得隨機很多,從池中獲取的連接不可用的概率也隨之增大。所以選擇連接池化時,會單獨開闢線程來檢測所有連接的可用性,淘汰不可用的連接。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但這遠遠不夠,一方面因爲檢測線程執行的間歇性會有一定的滯後,另一方面剛分配的可用連接下一刻也可能隨機不可用了。極致模式如何進一步提供連接的可用性?在獲取連接後與發送前的時間點上,會再一次檢查連接的可用性,發現連接不用時,重新申請新的連接作爲該連接的副本,可用副本會替換不可用的連接做接下來的事情,完成整個交互流程後所有副本和第一次獲取的連接作爲整體共同釋放。正是對網絡資源這樣的管理,使得連接幾乎達到100% 的可用性,同時避免因爲連接不可用導致整個業務的流程的阻塞或者將重試交給業務代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.2 極致的零拷貝"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/37\/37bc61b533271b9c12b04d1bb779a927.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步模式下會將文本內容和文件內容無差別地加載至堆內內存,經發送緩衝區傳輸到物理網絡鏈路。文件經過磁盤加載至內核空間,再將內核空間的數據拷貝到程序運行空間,經過程序再次拷貝回內核空間,交給網卡發送到物理鏈路,如果文件內容比較大,這是個很耗時間和空間的過程。幸運的是,操作系統提供一些更合理的系統調用,將文件內容從磁盤直接發送到發送緩衝區,避免冗餘的數據拷貝。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣,文本內容也存在一些“性能損失”,發送的首部時,有個首部Content-Length:16700,爲了計算整個發送報文的長度,只能把全部內容提前加載的內存累加統計。當首部發送完,內容體正式發送時,需要再次把文本的內容加載到內存。將其中的部分內容進行緩存,內容體發送時,直接從緩存中獲取,對整個發送報文,是一個混合數據流,針對性質不同的內容擴展不同的優化方式,充分發揮不同的流傳輸的優點,目的都是儘可能地減少數據的拷貝,在“場景1” 中有四種分別來自緩存流,文件流,內存流的數據源。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/a2\/a20e8b7117bfb2c77836778fc13c6069.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4.3 極致的非阻塞"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/d3\/d3e87d99f6c7299ec2c9539b0a3b3041.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同步模式網絡交互和業務邏輯都是阻塞的;異步模式網絡交互是非阻塞的,業務邏輯通過擴展future 實現異步化,當業務邏輯本身存在多個層次依賴時,代碼組織變得很複雜。爲了簡化,常常阻塞網絡獲得響應結果,同時犧牲了性能。爲了追求業務邏輯的足夠異步化同時使編程模型簡單,極致模式定義了網絡交互的事件鏈模型。一個層次的業務邏輯對應一個Call,網絡交互返回唯一的Back,Back 提供兩個API :thencall ,thcallAsync 。 thencall 自動將其內部邏輯封裝成Call 並且觸發執行或者由Back 觸發執行。thcallAsync 切換線程的上下文,將Call 的內部邏輯交給內置線程池完成。事件鏈的模型使得編碼更加輕鬆,開發人員只關心內部邏輯的實現而不關心處於何種狀態和模型下,更重要的是事件鏈的模型更好的保持了NIO的異步性,使得業務邏輯成爲TCP 雙管道特性的延伸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"極致的性能不只是發揮TCP 性能的優勢,還結合了上層邏輯併發控制。模型主要定義兩種之間的關係:位置關係和依賴關係。如上圖,Back 是Call A ,Call D 的依賴Call,Call A 是Call B,Call C 的依賴Call。Call D 是Call E 的前驅Call,是一種位置關係。Call A 可以與Call D 並行,Call B 與Call C 可以並行,Back 的完成可以驅動Call A,D。位置關係影響Call 執行的順序,依賴關係是Call 執行的條件。事件鏈模型本質是併發控制的框架,賦予獲取網絡結果後的業務邏輯的併發和傳導能力。無網絡交互的場景下也可以使用事件鏈模型,這對整個系統的代碼維護很有幫助。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"embedcomp","attrs":{"type":"table","data":{"content":"\n\n\n\n\n

\n

 \n\n

\n

I\/O 模型

\n\n

\n

線程模型

\n\n

\n

內存模型

\n\n

\n

編程模型

\n\n

\n

特點

\n\n

\n

代表組件

\n\n\n

\n

同步模式

\n\n

\n

阻塞IO

\n\n

\n

單線程

\n\n

\n

堆內存

\n\n

\n

同步輪詢

\n\n

\n

簡單,性能差

\n\n

\n

apache\n httpclient

\n\n\n

\n

異步模式

\n\n

\n

多路複用

\n\n

\n

多主Reactor

\n\n

\n

堆內存

\n\n

\n

事件驅動

\n\n

\n

高併發,邏輯複雜

\n\n

\n

apache

\n

httpasyncclient

\n\n\n

\n

極致模式

\n\n

\n

多路複用

\n\n

\n

多主\/靈活

\n\n

\n

內存池

\n\n

\n

事件鏈

\n\n

\n

極致性能,編程友好

\n\n

\n

besthttp

\n\n\n"}}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文章代碼主要使用組件的MAVEN 座標"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【同步模式】"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"org.apache.httpcomponents"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"httpclient"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.5.6"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【異步模式】"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"org.apache.httpcomponents"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"httpasyncclient"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4.1.4"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【極致模式】"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"io.github.nixiantongxue"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"nio-http"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"0.1.23-release"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者簡介:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"尼先,蘇寧IT總部,高級架構師,在線辦公產品矩陣技術負責人。關注技術理論和實踐持續探討以及雲時代企業信息流協作,安全,重塑的產品變革。"}]}]}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章