Go vs Java:为什么 Go 适合短平快,Java 统治大厂?这篇讲透了

这是经AI润色后的文章,底部附有我原创的初稿。我个人偏爱初稿,毕竟是自己的创作。

不知你更喜欢原稿,还是AI润色后的版本?原因是什么?欢迎在评论区分享,期待倾听大家的想法 。

Go 确实比 Java 更适合开发短平快的中小企业业务

Go 启动快,镜像小,搭配 Kubernetes,动态扩容缩容堪称无敌。

Java 更适合大型业务场景,但就像数据库百亿数据的调优一样,普通开发者很少能接触到这种级别。Java 的“重”不仅体现在镜像动辄几百 MB,启动耗时几秒,更深层次的原因是历史遗留问题及其带来的其他收益。

Java 有一大机制是 JIT(Just-In-Time 编译器),能根据业务场景动态优化字节码到汇编的过程,这是所谓的“热点优化”。Go 则没有这个机制,它依赖编译器优化,但编译器并非万能,无法完全适配真实的业务流量和压力。

顺带一提,Oracle JDK 其实就是 OpenJDK 的“亲兄弟”。OpenJDK 的核心维护团队基本来自 Oracle,Oracle JDK 只不过多了一些性能检测工具、专用垃圾回收器以及官方售后服务。在 JIT 优化上,Oracle JDK 和 OpenJDK 没有区别,二者完全一致。这也说明,JIT 这种重量级功能不是社区“用爱发电”的产物,而是靠大量真实业务数据和专业商业团队支撑的。

Tomcat 经常需要手动调参,而 Oracle 的 WebLogic 容器则内置流量分析工具,能自动调整线程池容量,无需过多干预。同样,MySQL 常需分库分表,而 Oracle 数据库单服务器就足够抗打。

我想说的是,Java 远比我们想象的强大,只是普通开发者用不到它的全部潜力。Java 更适合顶级电商、金融等场景,而且这些场景通常会混合开发,Go 和 Java 搭配使用。


Q: 什么是 NIO 和 BIO?

A:

  • NIO(Non-blocking I/O):异步 IO
  • BIO(Blocking I/O):阻塞 IO
BIO 的行为

Tomcat 默认使用 BIO,一个线程处理一个请求。当 Java HTTP 服务器接收客户端请求时,Tomcat 从线程池拉出一条线程,执行 doGetdoPost 逻辑。如果业务涉及数据库连接(通常需要毫秒级等待),这个线程就只能“傻等”。

高并发时,线程池很快被耗尽,服务器无法处理更多请求。传统解决办法是扩容,增加服务器数量。

NIO 的行为

NIO 允许一个线程处理多个请求。在连接数据库的空档,线程可以转而处理其他客户端请求。相比 BIO,NIO 减少了线程内存开销,即使百万并发也不会轻易出现 “out of memory”。

线程开销说明:Windows 下线程需要 TSS、TEB、TLS 以及 0 环和 3 环的栈空间,内存占用极大。如果每个请求分配一个线程,服务器内存很快就会不足。

NIO 的底层实现
  • C/C++ 原生:Linux 用 epoll(2002,前身是 select),Windows 用 IOCP(1994),使用都较繁琐。
  • Java 的 NIO:需使用 NIO Servlet 或 WebFlux,学习成本高,且国内生态支持不足。Spring Boot 开发的多数仍是 BIO 应用。
  • Go 的优势:原生支持 NIO,对 epollIOCP 封装完善,开箱即用。大多数 Go HTTP 服务器都支持 NIO,因此并发性能通常优于 Java 的 BIO。
破局:Java 21 的 Loom

Java 21 推出虚拟线程(Virtual Thread, VT),大幅降低线程内存开销。无需修改老 BIO 代码,Spring Boot 加一行配置即可启用 VT,并发性能直逼 WebFlux。

NIO 的应用

Redis 就是典型的 NIO 实现。


为什么 Java Servlet 出生时不是 NIO?

Servlet(1998)诞生时,Java 标准库还不支持 NIO。

为什么 Java 服务器开发需要 JVM + Servlet 容器 + Servlet 应用这种“怪组合”,而不是像 Go 那样直接一个可执行文件?

这是历史遗留问题。容器设计之初功能丰富,但如今许多功能已显得冗余。

Java 的发家史

Java 早期靠 Applet(类似 JavaScript 的浏览器端脚本语言)起家,后被 JS 取代。“Applet”意为“小应用”,Servlet 则是“小型服务器端应用”。

Java 并非一开始就瞄准服务端开发,它还能用 Swing 开发 GUI、支持 Applet。如果当时将 HTTP 功能嵌入 JVM,会让 JVM 过于臃肿。

于是,Java 官方只制定了 Servlet 规范,具体实现交给社区和厂商:社区推出了 Tomcat,商用领域则有了 WebLogic。

容器的功能变迁

容器曾提供集群 Session 同步(现被 Redis 取代)、负载均衡(现被 Nginx 和 Gateway 取代)。如今,容器层确实显得多余,Go 已彻底抛弃容器。

Java 当年为何牛逼?

Java 的竞品当时都很弱:

  • Python 无商业驱动,还在完善;
  • C/C++ 开发繁琐;
  • Java 凭借 JVM 跨平台、标准库完善、SUN 公司背书,一出道就光芒四射,迅速奠定了传奇地位。

原稿

Java vs Go

go 确实比 java更适合开发短平快的中小企业的业务

go 启动快,镜像小,搭配 k8s,动态扩容缩容是无敌的

java 适合大型业务场景,但就跟数据库的百亿数据调优一样,正常是我们触及不到的

java 过重,镜像动不动就几百MB,启动动不动就好几秒。但它的过重不仅仅只是历史遗留问题,其还带来的其他方面的收益

java 有个机制是 JIT,它可以根据业务场景动态优化, java 字节码转换的汇编的过程,这是热点优化

go 没有这个机制,go依赖编译器优化,但编译器不是万能的,不可能考虑到真正业务场景的流量和压力

顺带一提,oracle jdk 其实就是 openJDK,openJDK 的核心维护人员都是 oracle 的,oracle jdk 只比 openJDK 多了一些性能检测工具,专用的垃圾回收器以及 oracle 的官方售后服务

oracle jdk 没有比 open jdk 有任何的 JIT 的优化,它们用得都是一样的。也就是说 JIT 这种重量级的玩意不是社区用爱发电的牛人搓出来的,是靠大量真实的业务数据和专业的商业团队维护的。

Tomcat经常要进行参数调优,oracle的 weblogic 容器不需要,因为其内部内置流量分析工具,自动调整机器的线程池容量。

MySQL 经常要分库分表,oracle 是不需要的,直接单服务器就非常抗打

我想表达的是,java 远远比我们想象的要强大,只是我们用不到。java 更适合的场景是顶级电商,金融。而且那种场景正常都是混合开发了,go 和 java 混合用了。

##什么是BIO NIO?

BIO 就是阻塞IO,NIO 就是异步IO。

BIO行为:

Tomcat默认是BIO的,一个线程处理一个请求。

当我们的JAVA HTTP服务器处理客户端请求的时候,Tomcat会从线程池中拉一条线程出来,进行请求处理。

Tomcat会用这个线程来运行我们的doGet、doPost逻辑。当我们的业务逻辑需要进行数据库链接的时候,这个数据库网络链接,通常需要毫秒级别的等待,而这个线程只会傻傻地等。

这会导致一个客户请求就会占用一个请求,随着并发量的增大,线程池被耗尽,我们的HTTP服务器就无法承受更多的请求了,传统的解决办法是直接扩容,多架设几台服务器来抗。

NIO的行为:

NIO 可以一个线程处理多个请求。

NIO的HTTP应用程序,线程在我们链接数据库的空挡,会去处理其他客户的请求。

NIO比起BIO,减少了线程的内存开销,百万并发下也不会 out of memeory。

我们先提下线程的开销,线程在Windows下需要: TSS,TEB,TLS,0环和三环的栈空间。非常占用内存。

如果一个请求一个线程,就会导致服务器的内存不够用。

NIO的C C++ native实现:
  • linux下的epoll (2002),前身是select
  • windowd下的IOCP (1994)

这些使用都很繁琐。

java 和 go 的 NIO

java实现NIO,需要使用NIO servlet,或者使用webflux,都有很大的学习成本,而且国内没有相关生态。基本上用springboot开发的,都是BIO应用。

GO原生对NIO支持非常好,出生就含着"金钥匙",对IOCP、epoll进行了封装,开箱即用。大部分GO开发的HTTP服务器都支持NIO,所以GO的并发性能通常优于Java的BIO。

破局

java 21 推出了 loom,虚拟线程(VT),减小了线程的内存开销,无需修改原来的老BIO程序,springboot一行配置开启VT,直接并发性能就逼近webflux。

NIO的应用: Redis

为什么java servlet不出生就设计为NIO?

servlet(1998) 诞生的时候,java的标准库还不支持NIO。

为什么开发服务器需要JVM + servlet容器 + servlet应用,这种怪异的组合来实现? 而不是直接像go,一个exe就好了,为什么中间要套一个容器,为什么不直接把容器嵌入到JVM中?

其实是历史遗留问题,容器有很多现在用不到的功能。

这里就要提到java的发家史了,java早期
java是靠applet,一个类似于javascript,跑在浏览器端的脚本语言发家的,后来被js取代。

applet,let是一个后缀,通常是指小东西,booklet,小册子,servelet,小server端应用。

java当时不是完全像GO一样,设计出来,瞄准的就是服务端开发,java可以swing开发GUI,开发applet。现在的java场景基本上都是服务端开发了,但当时如果往JVM嵌入HTTP内容,会让JVM冗余。

java官方只是制定了servlet规范,剩下的都是让厂家发挥了。社区诞生了Tomcat,商用容器诞生了weblogic。

其实容器的功能其实非常多,集群session同步(被redis取代),集群负载均衡(被NGINX和gateway取代)。目前容器层确实冗余,go已经没有容器了。

java当年为什么牛逼?

因为java的同时期竞品都很拉胯,出道就光芒四射,JVM跨平台,标准库完善,SUN公司背书。

同期竞品:

  • Python没有商业驱动,当时还在完善中。
  • C/C++开发极其繁琐。

java上来就把其他给打趴下了,于是就有了后面的java传奇故事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值