Visual Studio Code,這是要上天?

後臺回覆”加群“加入

公眾號專屬技術群

Visual Studio Code,这是要上天?

來源:http://1t.click/akuE

它作為一個開源項目,也吸引了無數第三方開發者和終端用戶,成為頂尖開源項目之一。它在功能上做到了夠用,體驗上做到了好用,更在擁有海量插件的情況下做到了簡潔流暢,實屬難能可貴。

我是 VS Code 用戶,同時也為它開發插件,插件市場裡的眾多 Java 插件基本都是我們團隊的作品,所以我在日常工作中觀察到不少 VS Code 在工程方面的亮點,下面就來逐一探討。

簡潔而聚焦的產品定位,貫穿始終

你知道 VS Code 的開發團隊人數只有二十出頭嗎?

難以相信吧,大家都覺得 VS Code 無所不能,如此強大的工具那麼幾個人怎麼做得出來。實際上功能豐富是個美好的錯覺,因為大部分針對特定編程語言和技術的功能都是第三方插件提供的,VS Code 的核心始終非常精簡,這很考驗產品團隊的拿捏能力:做多了,臃腫,人手也不夠;做少了,太弱,沒人用。

他們團隊選擇了專注於核心功能的開發,為用戶提供簡潔流暢的體驗,並將該思路

貫穿在產品開發的每個環節。在我看來,這就是第一個亮點。

第一個亮點同時也是一個難點,因為“簡潔”說到底是產品的“形態”,更關鍵的其實是前置問題——產品的定位,它到底解決什麼問題。該問題如果從用戶的角度來看,可以轉換為以下兩點:

1.我們為什麼需要一個新的工具?

2.它到底是代碼編輯器(Editor)還是集成開發環境(IDE)?

讓我們來看看項目負責人Erich Gamma 的說法:

Visual Studio Code,这是要上天?

(視頻截圖 - Erich 闡述了VS Code的定位:編輯器+代碼理解+調試)

這張截圖它闡述了 VS Code 的定位:編輯器+代碼理解+調試。這是一個非常節制而平衡的選擇,專注於開發者“最常用”的功能,同時在產品的形式上力求簡潔高效。從結果來看,這個定位是相當成功的。

在這個定位的指導下,這二十多位工程師搞出了 VS Code。相對較小的功能集,使得開發者們能在代碼質量上精益求精,最終用戶們也得到了一個性能優異的工具,這是 VS Code 從一眾編輯器中脫穎而出的重要原因。

正因為產品定位以及團隊職責上的高度節制,團隊成員才能把時間花在這類問題上,寫出經得起考驗的代碼。

與此同時,較小的團隊也使得團隊成員做到了行為層面的整齊劃一,這點在社區互動上體現得尤為明顯,大家可以去 GitHub 上看他們的 Issues,超出產品定位範疇的請求和反饋基本都被婉拒或者轉交到第三方插件項目,可以說是很專注了。

看到這裡,似乎一切都好,但問題來了,碼農千千萬,你用 Node 我用 Go,你搞前端我弄後臺,VS Code 如何滿這些五花八門的需求呢?機智的你已經搶答了——海量插件。那麼接下來我們來深究一下 VS Code 是如何經營一個龐大的插件生態的。

進程隔離的插件模型

通過插件來擴展功能的做法已經是司空見慣了,但如何保證插件和原生功能一樣優秀呢?歷史告訴我們:不能保證:)。

大家可以參考 Eclipse,插件模型可以說是做得非常徹底了,功能層面也是無所不能,但存在幾個煩人的問題:不穩定、難用、慢,所以不少用戶轉投 IntelliJ 的懷抱。可謂成也插件,敗也插件。

問題的本質在於信息不對稱,它導致不同團隊寫出來的代碼,無論是思路還是質量,都不一致。最終,用戶得到了一個又亂又卡的產品。所以要讓插件在穩定性、速度和體驗的層面都做到和原生功能統一,只能是一個美好的願望。

來看看其他 IDE 是怎麼做的,Visual Studio 自己搞定所有功能,並且做到優秀,讓別人無事可做,這也成就了其“宇宙第一IDE”的美名;IntelliJ 與之相仿,開箱即用,插件可有可無。這麼看起來,自己搞定所有的事情是個好辦法,但大家是否知道,Visual Studio 背後有上千人的工程團隊,顯然,這不是 VS Code 這二十幾號人能搞定的。他們選擇了讓大家來做插件,那怎麼解決 Eclipse 所遇到的問題呢?

這裡分享一個小知識——Eclipse 核心部分的開發者就是早期的 VS Code 團隊。嗯,所以他們沒有兩次踏入同一條河流。與 Eclipse 不同, VS Code 選擇了把插件關進盒子裡。

這樣做首先解決的問題就是穩定性,這個問題對於 VS Code 來說尤為重要。都知道 VS Code 基於 Electron,實質上是個 Node.js 環境,單線程,任何代碼崩了都是災難性後果。所以 VS Code 乾脆不信任任何人,把插件們放到單獨的進程裡,任你折騰,主程序妥妥的。

Visual Studio Code,这是要上天?

插件與主進程隔離

VS Code 團隊的這一決策不是沒有原因的,正如前面提到的,團隊裡很多人其實是 Eclipse 的舊部,自然對 Eclipse 的插件模型有深入的思考。Eclipse 的設計目標之一就是把組件化推向極致,所以很多核心功能都是用插件的形式來實現的。

遺憾的是,Eclipse 的插件運行在主進程中,任何插件性能不佳或者不穩定,都直接影響到 Eclipse,最終結果是大家抱怨 Eclipse 臃腫、慢、不穩定。VS Code 基於進程做到了物理級別的隔離,成功解決了該問題。實際上進程級別的隔離也帶出了另一個話題,那就是界面與業務邏輯的隔離。

UI 渲染與業務邏輯隔離,一致的用戶體驗

“不穩定”之後的問題是“難用”,具體來說就是混亂的界面和流程,究其原因就是插件之間的界面語言的“不一致”,它導致學習曲線異常陡峭,並且在面臨問題時沒有統一的解決路徑。VS Code 的做法是根本不給插件們“發明”新界面的機會。

如上圖,插件們被關在 Extension Host 進程裡,而 UI 則在主進程裡,所以插件們天然沒法直接在用戶界面上做手腳。

VS Code 統管所有用戶交互入口,制定交互的標準,所有用戶的操作被轉化為各種請求發送給插件,插件能做的就是響應這些請求,專注於業務邏輯。但從始至終,插件都不能“決定”或者“影響”界面元素如何被渲染(顏色、字體等,一概不行),至於彈對話框什麼的,就更是天方夜譚了。

VS Code 對於用戶界面的把控可以說是謹慎到變態,做過插件的人都懂的,感興趣的同學可以去深挖一下 TreeView 的歷史,會有更直觀的體會。乍一看,第三方開發者被卡得死死的,這樣不是限制了大家的創造力嗎?

我想說這個做法跟這個團隊的背景密切相關,換一撥人很有可能會失敗。他們之所以能成功,是因為該團隊在開發工具領域深耕多年,他們把經驗轉換為觀點,最終落實到了 VS Code 的界面元素以及交互語言上,從結果來看,廣受歡迎。

界面和業務邏輯的徹底隔離,使得所有插件有了一致的行為,用戶就得到了整齊劃一的體驗。不僅如此,這種接口和行為層面的一致性,最終轉化成了另一個“偉大”的功能——Remote Development,我們稍後討論。接下來我們要聊的是 VS Code 另一個創舉——Language Server Protocol。

LSP——基於文本的協議

前文提到了 VS Code 定位中的兩個特色:代碼理解和調試,絕大部分都由第三方插件來實現,中間的橋樑就是兩大協議——Language Server Protocol(LSP)和 Debug Adapter Protocol(DAP)。兩者從設計的角度來看高度相似,我們著重看一下最火的 LSP。

首先,為什麼需要 LSP?

全棧開發早已成為這個時代的主流,軟件從業者們也越來越不被某個特定的語言或者技術所侷限,這也對我們手裡的金剛鑽提出了新的挑戰。

舉個栗子,我用 TypeScript 和 Node.js 做前端,同時用 Java 寫後臺,偶爾也用 Python 做一些數據分析,那麼我很有可能需要若干工具的組合,這樣做的問題就在於需要在工具間頻繁切換,無論從系統資源消耗和用戶體驗的角度來看,都是低效的。

那麼有沒有一種工具能在同一個工作區裡把三個語言都搞定呢?沒錯,就是 VS Code——支持多語言的開發環境,而多語言支持的基礎就是Language Server Protocol(LSP)。

該協議在短短几年內取得了空前的成功,到目前為止,已經有來自微軟等大廠以及社區的一百個實現,基本覆蓋了所有主流編程語言。同時,它也被其他開發工具所採納,比如 Atom、Vim、Sublime、Emacs、Visual Studio 和 Eclipse,從另一個角度證明了它的優秀。

更難能可貴的是,該協議還做到了輕量和快速,可以說是 VS Code 的殺手級特性了,同時也是微軟最重要的 IP 之一。。。哇塞,又強大又輕巧,怎麼看都是個騙局啊,那我們就來看看它到底怎麼做到的。

先劃重點:1、節制的設計 2、合理的抽象 2、周全的細節。

先來說說設計(Design),大而全是很常見的問題。如果讓我來設計這麼一個用來支持所有編程語言的東西,第一反應很可能是搞個涵蓋所有語言特性的超集。

微軟就有過這樣的嘗試,比如 Roslyn——一個語言中立的編譯器,C# 和 VB.NET 的編譯器都是基於它做的。大家都知道 C# 在語言特性層面是非常豐富的,Roslyn 能撐起 C# 足以說明它的強大。

那麼問題來了,為啥它沒有在社區得到廣泛應用呢?我想根本原因是“強大”所帶來的副作用:複雜、主觀(Opinionated)。光是語法樹就已經很複雜了,其他各種特性以及他們之間的關係更是讓人望而卻步,這樣一個龐然大物,普通開發者是不會輕易去碰的。

相較之下,LSP 顯然把小巧作為設計目標之一,它選擇做最小子集,貫徹了團隊一貫節制的作風。它關心的是用戶在編輯代碼時最經常處理的物理實體(比如文件、目錄)和狀態(光標位置)。它根本沒有試圖去理解語言的特性,編譯也不是它所關心的問題,所以自然不會涉及語法樹一類的複雜概念。

它也不是一步到位的,而是隨著 VS Code 功能的迭代而逐步發展的。所以它自誕生至今依然保持著小巧的身材,易懂,實現門檻也很低,迅速在社區得到了廣泛的支持,各種語言的 Language Server(LS)遍地開花。

小歸小,功能可不能少,所以抽象就非常關鍵了。LSP 最重要的概念是動作和位置,LSP 的大部分請求都是在表達”在指定位置執行規定動作“。

舉個栗子,用戶把鼠標懸停在某個類名上方,查看相關的定義和文檔。這時 VS Code 會發送一個'textDocument/hover'請求給 LS,這個請求裡最關鍵的信息就是當前的文檔和光標的位置。

LS 收到請求之後,經過一系列內部計算(識別出光標位置所對應的符號,並找出相關文檔),找出相關的信息,然後發回給 VS Code 顯示給用戶看。這樣一來一回的交互,在 LSP 裡被抽象成請求(Request)和回覆(Response),LSP 同時也規定了它們的規格(Schema)。

在開發者看來,概念非常少,交互形式也很簡單,實現起來非常輕鬆。

看到這裡,大家應該對 LSP 有了更進一步的理解,它本質上是膠水,把 VS Code 和各種語言的 LS 粘在一起。但它不是普通的膠水,而是非常有品位的膠水,這品位就體現在細節上。

首先這是一個基於文本的協議,文本降低了理解和調試的難度。參考 HTTP 和 REST 的成功,很難想象如果這是一個二進制協議會是什麼局面,甚至同樣是文本協議的 SOAP 也早已作古,足以說明“簡單”在打造開發者生態裡的重要性。

其次這是一個基於 JSON 的協議,JSON 可以說是最易讀的結構化數據格式了。大家看看各個代碼倉庫裡的配置文件都是啥格式就知道這是個多麼正確的決定了,現在還有人在新項目裡用 XML 嗎?又一次——“簡單”。

再次,這是一個基於 JSONRPC 的協議。由於 JSON 的流行,各大語言都對它有極好的支持,所以開發者根本不需要處理序列化、反序列化一類的問題,這是實現層面的“簡單”。

從這些細節可以看出,VS Code 團隊對當今技術趨勢的把握是相當精準的,他們決策充分考慮到了“簡單”,牢牢抓住了社區開發者的心。所以重要的事情說三遍:

在做設計的時候一定要傾向於簡單。

在做設計的時候一定要傾向於簡單。

在做設計的時候一定要傾向於簡單。

集大成的 Remote Development

今年五月,VS Code 發佈了 Remote Development(VSCRD),有了它,我們可以在遠程環境(比如虛機、容器)裡開一個 VS Code 工作區,然後用本地的 VS Code 連上去工作,下圖說明了它的運行模式:

Visual Studio Code,这是要上天?

VSCRD 從本質上改善了遠程開發的體驗,與常用的遠程桌面共享相比,具體改進如下:

響應迅速:VSCRD 所有的交互都在本地 UI 內完成,響應迅速;遠程桌面由於傳輸的是截屏畫面,數據往返延遲很大,卡頓是常態。

沿用本地設置:VSCRD 的 UI 運行在本地,遵從所有本地設置,所以你依然可以使用自己所習慣的快捷鍵、佈局、字體,避免了工作效率層面的開銷。

數據傳輸開銷小:遠程桌面傳輸的是視頻數據,而 VS Code 傳輸是操作請求和響應,開銷與命令行相仿,卡頓的情況進一步改善。

第三方插件可用:在遠程工作區裡,不僅VS Code的原生功能可用,所有第三方插件的功能依然可用;遠程桌面的話,你得自己一個個裝好。

遠程文件系統可用:遠程文件系統被完整映射到本地,這個兩者差不多。

那麼 VSCRD 做了什麼神奇的操作能夠實現以上效果呢?來看看它的架構圖:

Visual Studio Code,这是要上天?

其實答案都在前文有所提及:

進程級別隔離的插件模型

Extension Host(也就是圖中的 VS Code Server)與主程序做到了物理級別的分離,那麼把 Extension Host 在遠程或者本地跑沒有本質的區別。

UI 渲染與插件邏輯隔離,整齊劃一的插件行為

所有的插件的 UI 都由 VS Code 統一渲染,所以插件裡面只有純業務邏輯,行為高度統一,跑在哪裡都沒區別。

高效的協議LSP

VS Code 的兩大協議 LSP、DAP 都非常精簡,天然適合網絡延遲高的情況,用在遠程開發上再適合不過。

VS Code 團隊在架構上的決策無疑是非常有前瞻性的,與此同時,他們對細節的把握也是無可挑剔。正因為有了如此紮實的工程基礎,VSCRD 這樣的功能才得以誕生,所以我認為這是集大成的作品。

安利幾個 VSCRD 非常有用的場景:

開發環境配置起來很繁瑣,比如物聯網開發,需要自己安裝和配置各種工具和插件。在 VSCRD 裡,一個遠程工作區的模板即可搞定,如需安裝額外的工具,也就是改改 Dockerfile 的事情,非常簡單。在這裡可以找到常用的編程語言和場景的模板。

本地機器太弱,某些開發搞不了,比如機器學習,海量數據及和計算需求需要非常好的機器。在 VSCRD 裡,可以直接操作遠程文件系統,使用遠程計算資源。

最後

VS Code 像一顆耀眼的星星,吸引著成千上萬開發者為其添磚加瓦。

從 VS Code 的成功中,我們看到了好的設計和工程實踐能創造多少奇蹟。放眼軟件產業,各個層面的模式不斷被刷新,讓人激動之餘,也要求從業者不斷提高技能水平。

從個人學習的角度來看,瞭解這些模式誕生的前因後果,理解工程實踐中的決策過程是非常有利於提高工程能力的。

END

關注

公眾號(zhisheng)裡回覆 面經、ES、Flink、Spring、Java、Kafka、監控 等關鍵字可以查看更多關鍵字對應的文章

Flink 實戰

1、《從0到1學習Flink》—— Apache Flink 介紹

2、《從0到1學習Flink》—— Mac 上搭建 Flink 1.6.0 環境並構建運行簡單程序入門

3、《從0到1學習Flink》—— Flink 配置文件詳解

4、《從0到1學習Flink》—— Data Source 介紹

5、《從0到1學習Flink》—— 如何自定義 Data Source ?

6、《從0到1學習Flink》—— Data Sink 介紹

7、《從0到1學習Flink》—— 如何自定義 Data Sink ?

8、《從0到1學習Flink》—— Flink Data transformation(轉換)

9、《從0到1學習Flink》—— 介紹 Flink 中的 Stream Windows

10、《從0到1學習Flink》—— Flink 中的幾種 Time 詳解

11、《從0到1學習Flink》—— Flink 讀取 Kafka 數據寫入到 ElasticSearch

12、《從0到1學習Flink》—— Flink 項目如何運行?

13、《從0到1學習Flink》—— Flink 讀取 Kafka 數據寫入到 Kafka

14、《從0到1學習Flink》—— Flink JobManager 高可用性配置

15、《從0到1學習Flink》—— Flink parallelism 和 Slot 介紹

16、《從0到1學習Flink》—— Flink 讀取 Kafka 數據批量寫入到 MySQL

17、《從0到1學習Flink》—— Flink 讀取 Kafka 數據寫入到 RabbitMQ

18、《從0到1學習Flink》—— 你上傳的 jar 包藏到哪裡去了

19、大數據“重磅炸彈”——實時計算框架 Flink

20、《Flink 源碼解析》—— 源碼編譯運行

21、為什麼說流處理即未來?

22、OPPO數據中臺之基石:基於Flink SQL構建實數據倉庫

23、流計算框架 Flink 與 Storm 的性能對比

24、Flink狀態管理和容錯機制介紹

25、原理解析 | Apache Flink

結合 Kafka 構建端到端的 Exactly-Once 處理

26、Apache Flink 是如何管理好內存的?

27、《從0到1學習Flink》——Flink 中這樣管理配置,你知道?

28、《從0到1學習Flink》——Flink 不可以連續 Split(分流)?

29、Flink 從0到1學習—— 分享四本 Flink 的書和二十多篇 Paper 論文

30、360深度實踐:Flink與Storm協議級對比

31、Apache Flink 1.9 重大特性提前解讀

32、如何基於Flink+TensorFlow打造實時智能異常檢測平臺?只看這一篇就夠了

33、美團點評基於 Flink 的實時數倉建設實踐

34、Flink 靈魂兩百問,這誰頂得住?

35、一文搞懂 Flink 的 Exactly Once 和 At Least Once

36、你公司到底需不需要引入實時計算引擎?

37、Flink 從0到1學習 —— 如何使用 Side Output 來分流?

38、一文讓你徹底瞭解大數據實時計算引擎 Flink

39、基於 Flink 實現的商品實時推薦系統(附源碼)

40、如何使用 Flink 每天實時處理百億條日誌?

41、Flink 在趣頭條的應用與實踐

42、Flink Connector 深度解析

43、滴滴實時計算發展之路及平臺架構實踐

44、Flink Back Pressure(背壓)是怎麼實現的?有什麼絕妙之處?

45、Flink 實戰 | 貝殼找房基於Flink的實時平臺建設

46、如何使用 Kubernetes 部署 Flink 應用

47、一文徹底搞懂 Flink 網絡流控與反壓機制


分享到:


相關文章: