今年,公司面对包括大环境在内的不利局面,成功实现逆势增长,对人才的需求也进一步增加。为此,我们在这里总结一下公司系统产品方面的技术选型和路线。经过数年发展,不断地迭代和优化,目前我们的技术具有一些自己的特色。
因此,一方面,希望这篇文章能让即将加入公司的新同事能更好理解目前公司的技术选择;希望一起做事的人具有兼容的价值观;双向选择嘛,如果追求不同,大可各自择路。另一方面,也是增进与友商的沟通,让有兴趣的集成商或客户朋友了解相关技术的背景与利弊。
01、以Java为核心
选什么语言作为服务端开发的核心呢?选择还是蛮多的。
●不选C++,因为开发效率和生态。
●不选Python,因为Python压根就不是严肃的工程语言。比如,对于大型、复杂项目,最基本的,静态类型和强类型还是必须的。
●不选Node.js,因为虽然其各方面都好,上手快、开发效率高、机制现代(如非阻塞)、生态丰富(Git hub上各种库开发最活跃的语言),特别是配合Type Script,特别适合灵活性要求特别高的企业业务定制化开发。我们也曾长期使用Node.js,但目前不以其为核心出于两个原因:1、国内的强烈偏见;2、目前市面上的Node.js程序员多数处于初中级水平,特别是相对于Java等其他语言的程序员。
●不选Rust,因为学习曲线实在太陡峭。
可以说,目前服务器开发还是以Java、Go、C#最流行。而选择Java,最决定性的还是生态最强(简单说就是第三方库最丰富最强)。我们不像互联网,目前我们所在的行业必须考虑能够下管PLC,上管ERP;既要考虑多机负载均衡,也要考虑在单机上的运行……在我们这个行业,少不了系统对接集成。生态越丰富,风险越小。比如部分台系企业和外资企业的系统就有一些特点(古老、另类),某些行业有相对小众的系统接口或有特殊要求。目前Java的适应性相对最好,相比Go等新语言历史更悠久,使用更深入、方便。
虽然单从语言设计和进化上,笔者其实并不太喜欢Java。甚至从语言本身讲,C#比Java更好。对于部分工业领域,如OPC设备,C#的库也更好更多。可惜背后是微软。当然微软本身没错,一方面它也在积极面向开源;另一方面,封闭体系不一定就是差的,最典型的就是苹果生态和其开发生态。但目前的国内外形势,使得整个微软体系的技术风险大幅提升了,包括操作系统、语言、框架、数据库等各方面。行业内很多深度绑定微软体系的产品,如只能运行于Windows系统或只能使用SQL Server数据库的产品,在面临政府、国企、军工类项目时,未来还是有较大不确定性风险的。
Java相比于C#等语言还有一个优势,目前市场上高阶程序员更多。而其他语言,虽然容易招聘到初中级人员,但高级人员的数量相对较少。招聘难度大,技术团队风险就会相对较高。
02、以Kotlin为主语言
Java实际只是JVM语言的一种,除此之外还有Groovy、Scala以及JRuby、JPython等。这些语言都是为解决Java语言本身缺陷设计的。而且能利用Java生态的优势:直接使用现成的Java库。
在这些语言中,Kotlin是目前最好的一个。目前虽然不是所有Java开发者都知道Kotlin,但提到IDEA基本都知道:应该是目前最强大的Java IDE。而Kotlin和IDEA是同一家公司的作品。因此骨子里Kotlin是一门很“工程”的语言:它就是要更好更快的解决实际问题的,不花哨不废话。Kotlin在国外流行度很高。Android的官方首选语言也从Java变成了Kotlin。
Kotlin相比于Java有很多优势。首先是更现代。就如Swift与Objective-C的关系。另一个关键是更安全。比如,对于NPE这个Java世界中最常见、造成损失最多的异常。Java最新的几个版本也是遮遮掩掩“半修复”了这个问题。但Kotlin提供了更健壮的方案。
但我们最在乎的还是其开发效率,注意是开发效率不是运行效率。诸如类型推导、函数为一等公民(First-class)、函数式编程、函数默认值、数据类、语句作为表达式、单例类等特性,极大提高了表达力及开发效率。现实中,我们在迁移Java工程到Kotlin后,经常可以节省40%~70%的代码,是极其惊人的。更多Kotlin的优势,可以参见国外相关技术博客。
03、不用(没必要用)Spring生态
Spring生态几乎是目前Java开发的标配了。但是其必要性很大程度上是由于Java语言缺陷和Java开发者“向来喜欢把事情搞复杂”。如前所属,当主开发语言从Java切换到Kotlin后,我们发现对Spring的需求几乎可以忽略了。
这听上去似乎难以理解。但Java世界确实一直“过度复杂”。就像Spring Boot出现前,恐怕多数Java程序员都很难相信可以不用Tomcat/JBoss/Jetty这些Application Server Container。但Go/Node.js/Rust等语言的服务端程序,就跟那些非服务端程序一样,就是一个可执行文件,执行就是了,为什么还要一个单独的应用服务器?或者说,应用服务器需要那么复杂吗?Tomcat里那么多配置和功能,有几个人精通,实际又用到多少?引入过多概念、组件和功能,对于很多项目有些过度。
其实,在Spring之前,Java的服务端体系更复杂,简直是灾难(即早期的Java EE或者说EJB,特别是EJB2.x时代)。所以后面要推注解、推“约定大于配置”……所以,希望Java程序员不要“故步自封”,跳出去看看其他技术是怎么做的。也不要拿性能和复杂性当挡箭牌。一方面就事论事,我们这个行业多数项目对性能要求并不高。另一方面,已经有足够多的测试证明其他开发方式的性能优势,关键看会不会用、是否用的对。比如Node.js非阻塞的、多进程集群。
04、非阻塞、异步化与协程
我们接触了较多来自互联网、C端产品或纯系统类信息产品的开发者,发现剥去包装,他们实际只做过基于HTTP的通讯。对其他各类型I/O,甚至是文件读写,都不能很好掌握,更不用说写一个健壮的Socket服务器或客户端了。至于Modbus、OPC等更工业的协议,懂的人就更少了。
Java设计了一个很好的I/O体系,可是多少人没有深入研究。
我们并不像互联网产品一样,用户的并发可能有几万几十万甚至更多。虽然我们的并发用户数一般只有几个或者最多几百个。但我们在业务、设备方面要处理较大并发。比如,同时与上千个传感器通讯。比如,高频地控制设备。
并发并不意味着一定是“大”并发。实际上只要有超过一个线程/进程就可能存在并发问题。而目前我们这个行业的问题,更多是并发问题而不是“大”并发问题。比如,如何设计好一个计数器。比如,并发操纵高精度数字可能有哪些问题。先把基础打好再解决那些“宏大”的事情吧。
I/O和并发是我们较为关注的两项问题。在这两个问题上,我们既保守又激进。保守的是,我们认为必须先充分利用语言、核心库的基础功能,其他更好的工具、封装都是以这些为基础的,比如锁机制、通知、中断、执行器等。激进的是,我们也较为大胆的推动这个架构的非阻塞化、异步化。具体工具选择上,以Kotlin的协程为重点。虽然为此,对团队的要求显著上升,学习难度特别大。但从实测效果看,虽然高成本但确实高收益。(而且主要还是心态问题,比较像学生时代一样,心理抗拒学习,老师再怎么强压也没用。)
05、负载均衡与多机热备
首先,对于我们行业客户来说,这两个问题多数情况下是伪命题。
当出现单机性能瓶颈时,更短平快的解决方法是垂直扩展而不是水平扩展。即增加单机的性能而不是多增加机器。毕竟我们不像互联网,要考虑用户从几百个到几万个几千万个、多个数量级的跨越。我们处理的多数是设备、机器人增加了一到两个数量级。由此带来的算力缺口靠升级一下硬件性能即可实现,比如从一台几千块的台式机升级到上万或几万块的工作站或刀片机。
多机热备的动机是一台服务器挂了另一台能快速顶上,实现业务不中断。但,不宕机首先要做的难道不是尽量让程序不要出BUG而挂了吗?即,难道不应该先解决系统本身的质量吗?如果系统本身就不稳定,哪怕十台机器也有几率全挂掉。当然除了软件问题,还有操作系统和硬件宕机的可能。这么说来,网络还可能挂呢……提升可靠性的关键是消灭“单点故障”。即使服务器有多个备份,但整个链条中存在单一的没有备份的组件,那么系统整体的可靠性还是较低。即最薄弱的部分决定系统整体稳定性。
很多时候,这个行业只是为了均衡而均衡,为了多机而多机。从销售角度,毕竟可以多卖系统多卖服务器。从客户角度,多机备份是一种政治正确,至少我做了,万一出问题我也尽力了,表面功夫一定要做足。
实际上,做一个本身真支持负载均衡和多机热备的系统还是挺难的,有不少额外的工作量。毕竟涉及服务器进程、数据库、消息服务器、分布式缓存、反向代理服务器等多个组件。之前我们跟行业中多数友商一样,采用“外部”方案解决以上问题。虽然这些方案不是真热备,满足不了诸如秒级的业务停顿(目前需要分钟级的切换,实际你重启一下宕了服务器也差不多时间)。今年开始我们开始启动了一轮代码重构,目标是不靠外部方案,让系统自身具备“真の”支持负载均衡和多机热备的能力,以小于秒级的业务停顿为目标。
06、数据库
我们主要使用SQL数据库和Mongo DB。
我们不用存储过程。因为不想与任何特定数据库绑定,特别不想与SQL Server、Oracle等商业数据库绑定,毕竟今年的各种情况让我们懂得,“断供”这种事,或许并不远。所以,主流的SQL数据库我们都支持,像MySQL;当然这不难,因为很多工具可以做。
但我们也在减少SQL数据库的使用。更多的使用Mongo DB。核心理由只有一条:开发效率。特别是支撑复杂灵活持续变动的业务时的开发效率。这一点无法展开说,只有吃过一定苦才有可能深刻体会。当然Mongo DB等现代数据库有很多其他优势,比如从一开始就考虑高可用。以及支持Pipeline、Map Reduce等“大”数据技术。其实我们这个行业的数据多数介于“小”和“大”之间,多数情况下并不需要专门的大数据数据库和相关技术。能用一个工具解决的问题,不必用很多工具。
目前及不远的将来,我们还在努力要数据库这一层彻底透明化。不与任何特定数据库风格或产品绑定。
07、二次开发
我们这个行业离不开二次开发。
过去,包括我们,二次开发的技术必须与主产品的技术尽量一致。比如主产品使用Java语言开发,二次开发也必须用Java语言。但考虑到客户和集成商们有各自的专长和偏好。如果让他们用自己最熟悉的技术开发在效率和质量上肯定更好。于是,今年开始,我们后续产品将逐渐提供多语言SDK用于二次开发,包括C++、C#、Java、Python、Go、Node.js等。虽然HTTP、Web Service、微服务等技术和概念已经为二次开发提供了可能。但始终没有“用自己熟悉的语言直接调用函数”来得爽。我们多花点(不少)时间,让客户少花点(不少)时间。
(转载)