快捷搜索: 长连接 前端 源码 pan

go与java

从2018年开始创业,因为java非常熟悉,市场上java程序员非常多,方便招聘,首选java开发,从2018下半年开始,毅然决然新的项目使用go开发,是什么原因让我下定决心呢?

java的运行原理很简单,把源文件编译生成一种二进制中间码,存储在class文件中,然后再通过运行与操作系统平台环境相对应的Java虚拟机来运行class文件,执行编译产生的字节码,调用class文件中实现的方法来满足程序的Java API调用。后续android为了提升速度出了其他的字节码文件,和虚拟机不在本次讨论范围 java的前身是1992开发的Oak语言,1996年1月发布JDK 1.0,1999年4月发布HotSpot虚拟机,HotSpot成为了JDK 1.3及之后所有版本的Sun JDK的默认虚拟机

go发布于2009年,可以说java是20世纪的产物,go是21世纪的产物。跨世纪了[惊!]。

切换

我2008年在学习java的时候还是个学生,是有c和c++基础的(vc写过计算机,读取手机短信这种水平)。 用了3个月的时间才大致弄明白java做的事情。 弄明白了各种关键字作用,和面向对象的机制。实际去企业开发的时候还是会遇到各种问题。而限制使用范围的关键字也只用public和private。很少用连续的继承。 2018年我带着团队学习使用go的时候,只用2周时间大家就都掌握了,能填充代码开发了。 如果你是一个java程序员,从java到go的距离只有2周时间

开发

// java
@RestController
@RequestMapping(value = "/xx")
public class LotteryController {
          
   

    @RequestMapping(value = "/xx1", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
    public String xXXX1(HttpServletRequest httpRequest) {
          
   
        return "success";
    }
    @RequestMapping(value = "/xx2", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
    public String xXXX2(HttpServletRequest httpRequest) {
          
   
        return "success";
    }
}

使用iris框架写个post方法

// go
// 启动文件
app.Post("/xx/xx1", controller.Xxxx1)
app.Post("/xx/xx2", controller.Xxxx2)
// --------------------------------------
// 具体文件中方法实现
func Xxxx1(ctx context.Context) {
          
   
	ctx.Text("success")
}
func Xxxx2(ctx context.Context) {
          
   
	ctx.Text("success")
}

这个简单的例子可以看出 可以看出java spring的方法是注解在实现类中的,go是通过写在代码中的更方便。而且所有入口在一个文件中更方便查找。 对于杠精来说可以不用spring注解,使用spring配置文件,或者servlet。。。。 在处理IO时java用try catch finally,finally中还得分开try catch 关闭。我早就忍不了,我都怀疑强迫症的人会无限的写下去。。。 java启个线程

new Thread(new Runnable() {
            @Override
            public void run() {
      		// do         
            }
        }).start();

而go的语法就精简很多了,启动个协程运行,只要在掉用的时候加关键字go。

通过代码我们可以看出go的代码非常精简

运行

go的协程设计确实更适合高并发,java开一个线程就对应于一个系统线程。go开一个协程不对应一个系统线程。一个系统线程内存消耗一般2M,而协程只需要2k。1k个线程是2G内存。2G的内存能放多少协程啊

go的runtime与用户代码一起打包成可执行文件,java的代码需要依赖于虚拟机,open jdk 8的jre有100多Mb呢。

go的协程没有那么神秘,可以说实现的很简单。使用G-P-M模型 G:Goroutine用于存储运行堆栈、状态和任务函数可复用,需要绑定到P才能被掉用,Goroutine会排队等待执行。 P:Processor逻辑处理器,提供执行环境,内存分配状态,Goroutine队列等。P的数量由用户设置的GoMAXPROCS决定,最大数256,P的数量决定了并行执行G队列的数量。物理CPU核数 >= P的数量 M:Machine 系统内核线程的抽象,代表真正执行的计算资源,在绑定有效的P之后,进入schedule循环,schedule循环机制是P的G队列中获取要被执行的G,M的数量由Go RunTime调整,M不保留G的状态,这样G在推出M之后,下次可能由其他的M执行 Sched:Go调度器。负责维护G的总队列,和P中存在的G队列和状态信息,创建M,清理回收M等工作

性能测试 是骡子是马拉出来溜溜 使用ab命令压测同一台机器(2C4G 操作是根据id查询数据库中的一条记录) 请求次数100000 线程数150,为了避免干扰启动go服务的时候关闭java服务,反之亦然。

java环境: spring boot 2.2.5 + mybatis 3.5.3 hikari连接池 连接数200 tomcat设置:JAVA_OPTS="-Xms256m -Xmx1024m -XX:PermSize=64m -XX:MaxPermSize=128m"

java第一次 java第二次 java第三次 java第四次 java第五次 java的统计情况

go环境: iris v11.1.1 + gorm v1.9.10

go第一次

go第二次 go第三次 go第四次 go第五次

go的统计情况

说明 通过压测我们可以看出Java的第一次压测劣势非常明显,java是编译成class文件, 第一次执行需要通过虚拟机进行类加载,之后的执行可以用加载好的文件执行。所以我去掉java的第一次重新计算了一下平均值

java内存 :tomcat启动之前机器已使用内存是769M,启动之后是1.20G,压测时内存峰值1.35G,后面几次压测之后,内存回收,回到1.21G

go内存 :go启动之前机器已使用内存是768M,启动之后是775M, 压测时内存峰值804M,,内存回收,回到784M

数据项 java go 总耗时平均 22.36s 20.77s 每秒处理数 4474 4812 每条处理耗时 33.55ms 31.16ms 传输速率 1044k/s 1122k/s 压测过程中cpu使用率 96%-99% 96%-99% 启动内存占用 459Mb 7Mb 压测过程最高占用内存 613Mb 36Mb 执行结束后内存占用 470Mb 16Mb

性能测试部分go完胜

打包编译速度

我使用的是macOS cpu i5 内存 8G

go 编译linux下的包用时 <2s java maven 打war包用时 20s

docker image

构建docker image 的大小与方便程度决定了上线部署的速度 为了测试都基于ubuntu镜像

数据项 java go 最终镜像大小 151M 33.8M 操作步骤 1 添加jre和tomcat 2设置环境变量 3添加war包 添加可执行文件 优化空间 未知 使用scratch基础镜像体积更小

2019 发生了什么

go发布了两个版本1.12 和1.13 ,从1.13开始我才觉得go能正常使用了。之前go的使用有些弊端,首先打包就是个麻烦事。1.13版本Go Modules 默认成为了Golang 官方版本原生的包管理方式,包管理问题算是画上了一个句号。 还有一件非常重要的大事,之前在国内使用某系包,都会收到墙的影响,大家都是使用github上的源代替官方源,2019go有了官方承认的代理https://goproxy.cn,引包不再成为问题。

java发布了jdk12

Shenandoah GC算法,没发布在openjdk中。G1优化1(可中断 mixed GC)G1优化2 (归还不使用的内存),可以看出官方还是想改进垃圾回收。毕竟靠垃圾回收优化养活了Zing悲哀不悲哀 2019 的双11 阿里90%的容器都使用了Wisp2,这个Wisp2的最重要的一点就是给java安上了协程。真是笑出眼泪。

后续

java的库非常全面,也很好用。java程序员非常好招聘。垃圾回收一致被大家诟病,java也一直在这方面努力。 go后发优势明显,不过在2019之前因为墙和包管理的问题,非常难用。go的轮子还没有造那么多。不过正在快速补充进来。

经验分享 程序员 微信小程序 职场和发展