1.1 Java的历史
Java从诞生距今已有20多年了,长期占据着“天下第一”编程语言的宝座。虽然软件架构师和软件开发者可以选择许多技术来解决业务或技术问题。但Java仍然是当今构建应用程序最广泛使用的编程语言之一。Java拥有1200万的庞大开发者群体,全世界有450亿部物理设备使用着Java技术。同时,在云端数据中心的虚拟化环境里,还运行着超过250亿个Java虚拟机的进程实例,此数据来自Oracle的WebCast。
Java最初是面向智能设备的单片式计算机系统,但由于自创了一套字节码系统,无相应的硬件支持,这个项目就被搁置了。于是Java转入互联网,使网页具有动态效果,得到了大众的认可。1996年1月,Sun公司发布了Java的第一个开发工具包(JDK 1.0),这是Java发展历程中的重要里程碑,标志着Java成为一种独立的开发工具。
1998年12月8日,第二代Java平台发布,包括J2ME(Java 2 Micro Edition,Java 2平台的微型版,应用于移动、无线及有限资源的环境)、J2SE(Java 2 Standard Edition,Java 2平台的标准版,应用于桌面环境)、J2EE(Java 2 Enterprise Edition,Java 2平台的企业版),标志着Java的应用开始普及。这个时代最著名的就是EJB规范,Java进入了EJB时代。
在轻量级时代,由于笨重的EJB不太容易推广,由Rod Johnson发起,为解决开发者在J2EE开发中遇到的许多常见的问题,提出了不重复发明轮子,采用功能强大的IOC、AOP等技术实现轻量级的Java解决方案。Java进入了Spring时代。
Spring统治Java差不多20年,之后产生了许多基于Spring的专业组件,具有划时代意义的是Spring Boot的产生,之后进入微服务时代,又有Spring Cloud等产生。
在云原生时代,Java存在着危机。在过去的15~20年中,Java应用程序堆栈和Java虚拟机(JVM)进行了许多优化,以支持运行大型堆栈和在运行时做出决策的高度动态框架。但是由于云原生的出现,Java也面临着危机,Java与云原生的矛盾来源于Java诞生之初,植入其基因之中的一些基本的前提假设已经逐渐动摇,甚至不再成立。
云原生以操作系统层虚拟化的方式通过容器实现的不可变基础设施去解决矛盾,将程序连同它的运行环境一起封装到稳定的镜像里,现已是一种主流的应用程序分发方式。Docker同样提出过“一次构建,到处运行”(Build Once, Run Anywhere)的口号,所以Java技术“一次编译,到处运行”的优势已经被容器大幅度地削弱,不再是大多数服务端开发者技术选型的主要考虑因素了。
但Java面临的更大风险来自那些与技术潮流直接冲突的假设。譬如,Java总体上是面向大规模、长时间的服务端应用而设计的,严谨的语法有利于约束所有人写出较一致的代码;静态类型动态链接的语言结构有利于多人协作开发,让软件触及更大规模的应用;即时编译器、性能智能优化、垃圾收集子系统等Java最具代表性的技术特征,都是为了便于长时间运行的程序能享受到硬件规模发展的红利。
另一方面,在微服务的背景下,提倡服务围绕业务能力而非技术来构建应用,不再追求实现上的一致,一个系统由不同语言、不同技术框架所实现的服务来组成是完全合理的;服务化拆分后,很可能单个微服务不再需要面对数十GB、数百GB乃至TB的内存;有了高可用的服务集群,也无须追求单个服务要7×24小时不间断地运行,它们随时可以中断和更新。
同时,微服务又对应用的容器化亲和性方面提出了新的要求,容器化亲和性的内容包括镜像体积、内存消耗、启动速度,以及达到最高性能的时间等。Serverless也进一步增加了对这些因素的考虑权重,而这些却正好都是Java的弱项:哪怕再小的Java程序也要带着完整的虚拟机和标准类库,使得镜像拉取和容器创建效率降低,进而使整个容器生命周期拉长。基于Java虚拟机的执行机制,使得任何Java的程序都会有固定的基础内存开销及固定的启动时间,而且Java生态中广泛采用的依赖注入进一步将启动时间拉长,使得容器的冷启动时间很难缩短。
Java是在云、容器和容器编排系统(如Kubernetes)出现之前创建的。现在的时代是云原生时代。在这个时代,Java技术体系的许多前提假设都受到了挑战,如“一次编译,到处运行”“面向长时间大规模程序而设计”“从开放的代码空间中动态加载”“一切皆为对象”“统一线程模型”等均受到了挑战。
这些问题的出现说明需要重新打造新的Java生态系统。