今年,公司面對包括大環(huán)境在內的不利局面,成功實現(xiàn)逆勢增長,對人才的需求也進一步增加。為此,我們在這里總結一下公司系統(tǒng)產(chǎn)品方面的技術選型和路線。經(jīng)過數(shù)年發(fā)展,不斷地迭代和優(yōu)化,目前我們的技術具有一些自己的特色。
因此,一方面,希望這篇文章能讓即將加入公司的新同事能更好理解目前公司的技術選擇;希望一起做事的人具有兼容的價值觀;雙向選擇嘛,如果追求不同,大可各自擇路。另一方面,也是增進與友商的溝通,讓有興趣的集成商或客戶朋友了解相關技術的背景與利弊。
01、以Java為核心
選什么語言作為服務端開發(fā)的核心呢?選擇還是蠻多的。
●不選C++,因為開發(fā)效率和生態(tài)。
●不選Python,因為Python壓根就不是嚴肅的工程語言。比如,對于大型、復雜項目,最基本的,靜態(tài)類型和強類型還是必須的。
●不選Node.js,因為雖然其各方面都好,上手快、開發(fā)效率高、機制現(xiàn)代(如非阻塞)、生態(tài)豐富(Git hub上各種庫開發(fā)最活躍的語言),特別是配合Type Script,特別適合靈活性要求特別高的企業(yè)業(yè)務定制化開發(fā)。我們也曾長期使用Node.js,但目前不以其為核心出于兩個原因:1、國內的強烈偏見;2、目前市面上的Node.js程序員多數(shù)處于初中級水平,特別是相對于Java等其他語言的程序員。
●不選Rust,因為學習曲線實在太陡峭。
可以說,目前服務器開發(fā)還是以Java、Go、C#最流行。而選擇Java,最決定性的還是生態(tài)最強(簡單說就是第三方庫最豐富最強)。我們不像互聯(lián)網(wǎng),目前我們所在的行業(yè)必須考慮能夠下管PLC,上管ERP;既要考慮多機負載均衡,也要考慮在單機上的運行……在我們這個行業(yè),少不了系統(tǒng)對接集成。生態(tài)越豐富,風險越小。比如部分臺系企業(yè)和外資企業(yè)的系統(tǒng)就有一些特點(古老、另類),某些行業(yè)有相對小眾的系統(tǒng)接口或有特殊要求。目前Java的適應性相對最好,相比Go等新語言歷史更悠久,使用更深入、方便。
雖然單從語言設計和進化上,筆者其實并不太喜歡Java。甚至從語言本身講,C#比Java更好。對于部分工業(yè)領域,如OPC設備,C#的庫也更好更多??上П澈笫俏④?。當然微軟本身沒錯,一方面它也在積極面向開源;另一方面,封閉體系不一定就是差的,最典型的就是蘋果生態(tài)和其開發(fā)生態(tài)。但目前的國內外形勢,使得整個微軟體系的技術風險大幅提升了,包括操作系統(tǒng)、語言、框架、數(shù)據(jù)庫等各方面。行業(yè)內很多深度綁定微軟體系的產(chǎn)品,如只能運行于Windows系統(tǒng)或只能使用SQL Server數(shù)據(jù)庫的產(chǎn)品,在面臨政府、國企、軍工類項目時,未來還是有較大不確定性風險的。
Java相比于C#等語言還有一個優(yōu)勢,目前市場上高階程序員更多。而其他語言,雖然容易招聘到初中級人員,但高級人員的數(shù)量相對較少。招聘難度大,技術團隊風險就會相對較高。
02、以Kotlin為主語言
Java實際只是JVM語言的一種,除此之外還有Groovy、Scala以及JRuby、JPython等。這些語言都是為解決Java語言本身缺陷設計的。而且能利用Java生態(tài)的優(yōu)勢:直接使用現(xiàn)成的Java庫。
在這些語言中,Kotlin是目前最好的一個。目前雖然不是所有Java開發(fā)者都知道Kotlin,但提到IDEA基本都知道:應該是目前最強大的Java IDE。而Kotlin和IDEA是同一家公司的作品。因此骨子里Kotlin是一門很“工程”的語言:它就是要更好更快的解決實際問題的,不花哨不廢話。Kotlin在國外流行度很高。Android的官方首選語言也從Java變成了Kotlin。
Kotlin相比于Java有很多優(yōu)勢。首先是更現(xiàn)代。就如Swift與Objective-C的關系。另一個關鍵是更安全。比如,對于NPE這個Java世界中最常見、造成損失最多的異常。Java最新的幾個版本也是遮遮掩掩“半修復”了這個問題。但Kotlin提供了更健壯的方案。
但我們最在乎的還是其開發(fā)效率,注意是開發(fā)效率不是運行效率。諸如類型推導、函數(shù)為一等公民(First-class)、函數(shù)式編程、函數(shù)默認值、數(shù)據(jù)類、語句作為表達式、單例類等特性,極大提高了表達力及開發(fā)效率?,F(xiàn)實中,我們在遷移Java工程到Kotlin后,經(jīng)常可以節(jié)省40%~70%的代碼,是極其驚人的。更多Kotlin的優(yōu)勢,可以參見國外相關技術博客。
03、不用(沒必要用)Spring生態(tài)
Spring生態(tài)幾乎是目前Java開發(fā)的標配了。但是其必要性很大程度上是由于Java語言缺陷和Java開發(fā)者“向來喜歡把事情搞復雜”。如前所屬,當主開發(fā)語言從Java切換到Kotlin后,我們發(fā)現(xiàn)對Spring的需求幾乎可以忽略了。
這聽上去似乎難以理解。但Java世界確實一直“過度復雜”。就像Spring Boot出現(xiàn)前,恐怕多數(shù)Java程序員都很難相信可以不用Tomcat/JBoss/Jetty這些Application Server Container。但Go/Node.js/Rust等語言的服務端程序,就跟那些非服務端程序一樣,就是一個可執(zhí)行文件,執(zhí)行就是了,為什么還要一個單獨的應用服務器?或者說,應用服務器需要那么復雜嗎?Tomcat里那么多配置和功能,有幾個人精通,實際又用到多少?引入過多概念、組件和功能,對于很多項目有些過度。
其實,在Spring之前,Java的服務端體系更復雜,簡直是災難(即早期的Java EE或者說EJB,特別是EJB2.x時代)。所以后面要推注解、推“約定大于配置”……所以,希望Java程序員不要“故步自封”,跳出去看看其他技術是怎么做的。也不要拿性能和復雜性當擋箭牌。一方面就事論事,我們這個行業(yè)多數(shù)項目對性能要求并不高。另一方面,已經(jīng)有足夠多的測試證明其他開發(fā)方式的性能優(yōu)勢,關鍵看會不會用、是否用的對。比如Node.js非阻塞的、多進程集群。
04、非阻塞、異步化與協(xié)程
我們接觸了較多來自互聯(lián)網(wǎng)、C端產(chǎn)品或純系統(tǒng)類信息產(chǎn)品的開發(fā)者,發(fā)現(xiàn)剝去包裝,他們實際只做過基于HTTP的通訊。對其他各類型I/O,甚至是文件讀寫,都不能很好掌握,更不用說寫一個健壯的Socket服務器或客戶端了。至于Modbus、OPC等更工業(yè)的協(xié)議,懂的人就更少了。
Java設計了一個很好的I/O體系,可是多少人沒有深入研究。
我們并不像互聯(lián)網(wǎng)產(chǎn)品一樣,用戶的并發(fā)可能有幾萬幾十萬甚至更多。雖然我們的并發(fā)用戶數(shù)一般只有幾個或者最多幾百個。但我們在業(yè)務、設備方面要處理較大并發(fā)。比如,同時與上千個傳感器通訊。比如,高頻地控制設備。
并發(fā)并不意味著一定是“大”并發(fā)。實際上只要有超過一個線程/進程就可能存在并發(fā)問題。而目前我們這個行業(yè)的問題,更多是并發(fā)問題而不是“大”并發(fā)問題。比如,如何設計好一個計數(shù)器。比如,并發(fā)操縱高精度數(shù)字可能有哪些問題。先把基礎打好再解決那些“宏大”的事情吧。
I/O和并發(fā)是我們較為關注的兩項問題。在這兩個問題上,我們既保守又激進。保守的是,我們認為必須先充分利用語言、核心庫的基礎功能,其他更好的工具、封裝都是以這些為基礎的,比如鎖機制、通知、中斷、執(zhí)行器等。激進的是,我們也較為大膽的推動這個架構的非阻塞化、異步化。具體工具選擇上,以Kotlin的協(xié)程為重點。雖然為此,對團隊的要求顯著上升,學習難度特別大。但從實測效果看,雖然高成本但確實高收益。(而且主要還是心態(tài)問題,比較像學生時代一樣,心理抗拒學習,老師再怎么強壓也沒用。)
05、負載均衡與多機熱備
首先,對于我們行業(yè)客戶來說,這兩個問題多數(shù)情況下是偽命題。
當出現(xiàn)單機性能瓶頸時,更短平快的解決方法是垂直擴展而不是水平擴展。即增加單機的性能而不是多增加機器。畢竟我們不像互聯(lián)網(wǎng),要考慮用戶從幾百個到幾萬個幾千萬個、多個數(shù)量級的跨越。我們處理的多數(shù)是設備、機器人增加了一到兩個數(shù)量級。由此帶來的算力缺口靠升級一下硬件性能即可實現(xiàn),比如從一臺幾千塊的臺式機升級到上萬或幾萬塊的工作站或刀片機。
多機熱備的動機是一臺服務器掛了另一臺能快速頂上,實現(xiàn)業(yè)務不中斷。但,不宕機首先要做的難道不是盡量讓程序不要出BUG而掛了嗎?即,難道不應該先解決系統(tǒng)本身的質量嗎?如果系統(tǒng)本身就不穩(wěn)定,哪怕十臺機器也有幾率全掛掉。當然除了軟件問題,還有操作系統(tǒng)和硬件宕機的可能。這么說來,網(wǎng)絡還可能掛呢……提升可靠性的關鍵是消滅“單點故障”。即使服務器有多個備份,但整個鏈條中存在單一的沒有備份的組件,那么系統(tǒng)整體的可靠性還是較低。即最薄弱的部分決定系統(tǒng)整體穩(wěn)定性。
很多時候,這個行業(yè)只是為了均衡而均衡,為了多機而多機。從銷售角度,畢竟可以多賣系統(tǒng)多賣服務器。從客戶角度,多機備份是一種政治正確,至少我做了,萬一出問題我也盡力了,表面功夫一定要做足。
實際上,做一個本身真支持負載均衡和多機熱備的系統(tǒng)還是挺難的,有不少額外的工作量。畢竟涉及服務器進程、數(shù)據(jù)庫、消息服務器、分布式緩存、反向代理服務器等多個組件。之前我們跟行業(yè)中多數(shù)友商一樣,采用“外部”方案解決以上問題。雖然這些方案不是真熱備,滿足不了諸如秒級的業(yè)務停頓(目前需要分鐘級的切換,實際你重啟一下宕了服務器也差不多時間)。今年開始我們開始啟動了一輪代碼重構,目標是不靠外部方案,讓系統(tǒng)自身具備“真の”支持負載均衡和多機熱備的能力,以小于秒級的業(yè)務停頓為目標。
06、數(shù)據(jù)庫
我們主要使用SQL數(shù)據(jù)庫和Mongo DB。
我們不用存儲過程。因為不想與任何特定數(shù)據(jù)庫綁定,特別不想與SQL Server、Oracle等商業(yè)數(shù)據(jù)庫綁定,畢竟今年的各種情況讓我們懂得,“斷供”這種事,或許并不遠。所以,主流的SQL數(shù)據(jù)庫我們都支持,像MySQL;當然這不難,因為很多工具可以做。
但我們也在減少SQL數(shù)據(jù)庫的使用。更多的使用Mongo DB。核心理由只有一條:開發(fā)效率。特別是支撐復雜靈活持續(xù)變動的業(yè)務時的開發(fā)效率。這一點無法展開說,只有吃過一定苦才有可能深刻體會。當然Mongo DB等現(xiàn)代數(shù)據(jù)庫有很多其他優(yōu)勢,比如從一開始就考慮高可用。以及支持Pipeline、Map Reduce等“大”數(shù)據(jù)技術。其實我們這個行業(yè)的數(shù)據(jù)多數(shù)介于“小”和“大”之間,多數(shù)情況下并不需要專門的大數(shù)據(jù)數(shù)據(jù)庫和相關技術。能用一個工具解決的問題,不必用很多工具。
目前及不遠的將來,我們還在努力要數(shù)據(jù)庫這一層徹底透明化。不與任何特定數(shù)據(jù)庫風格或產(chǎn)品綁定。
07、二次開發(fā)
我們這個行業(yè)離不開二次開發(fā)。
過去,包括我們,二次開發(fā)的技術必須與主產(chǎn)品的技術盡量一致。比如主產(chǎn)品使用Java語言開發(fā),二次開發(fā)也必須用Java語言。但考慮到客戶和集成商們有各自的專長和偏好。如果讓他們用自己最熟悉的技術開發(fā)在效率和質量上肯定更好。于是,今年開始,我們后續(xù)產(chǎn)品將逐漸提供多語言SDK用于二次開發(fā),包括C++、C#、Java、Python、Go、Node.js等。雖然HTTP、Web Service、微服務等技術和概念已經(jīng)為二次開發(fā)提供了可能。但始終沒有“用自己熟悉的語言直接調用函數(shù)”來得爽。我們多花點(不少)時間,讓客戶少花點(不少)時間。
(轉載)