那个执事,争先:我如何于 2015 年在 Java Web 项目中推动 HTTP/2
2015 年 5 月,HTTP/2 发布。
2015 年第 3 季度,我所在企业的一个战略级客户(而且是第二大客户)说,他们需要在当年年底之前支持 HTTP/2(原因忘了,且与本文无关,从略)。
而在当时,Tomcat、Jetty、Undertow 等都还不支持 HTTP/2,Nginx 虽然已开始紧锣密鼓的添加对 HTTP/2 的支持,但等别人总不是个办法。依我对 Java 世界干什么都慢三拍的了解,一旦别人进度稍慢或者出现了隐蔽的坑,还是要自己来搞。更重要的原因是,我们需要更简单的部署方式,需要更好地应对弱网环境,作为一个中型企业,还需要形成现代的、可通用的、自主可控的技术积累。
求人不如求己,自己搞吧。
劝君免谈 Java EE
既然是搞 Java Web,那么 Servlet 总是被人们提起,作为 Java EE 中存在感最高的标准,它被众多 Servlet 容器所支持,在世界各地的机房闪闪发光。
而自从接触 Java 的第一天,我就在想,Servlet,乃至更大范围的 Java EE, 在这个年头到底还有什么用?
很多朋友可能知道,Java EE 是若干个标准的总称,包括而不限于:Servlet、JSP、EJB、JTA、JPA、JSF、JMS(完整列出来有好几十个)。随着服务化、多语言开发、前后端分离的兴起,Java EE 在技术比较新的公司(尤其是那几个知名的互联网公司)迅速被边缘化,其中的 Servlet 的重要性也日趋减小。要知道,标准的意义在于协作,但谁又会没事换容器玩?前后端、服务之间的调用一般通过 REST 或 RPC,前者与是否采用 Servlet 毫无关系,后者更没有 Servlet 什么事。
我大胆地下判断:是时候抛弃 Java EE 了。
事实上,后序几年的技术发展也印证了我的判断。
Java EE 被 Oracle 抛弃而独立发展,标志着不接地气的 Java EE 全面败给了 Spring 开源栈。
Spring Boot 内置容器方式使得 Java Web 程序在外部看来是 HTTP Server 而非 Servlet,如此一来,Servlet 容器的部分就可以被取代,例如,Spring 5 提供的 WebFlux 底层就可以在 netty 而非 Servlet 之上构建。
语言无关的基础设施的涌现,使得 Java EE 的用处越来越少。
越来越多的优秀 Java 工程师不再知道 Java EE 是什么。
当然,这些都是后话。当时我要做的,就是赶快把一个被定名为 Fxxxx 的框架搞出来,无论是否用 Servlet。
网络库上的抉择
我当时所任职的那家企业,有着深厚的桌面软件背景和服务器软件背景,内部有大量 C/C++ 基础设施,其中就包括高性能的 TCP、UDP 网络库。它分别在 Windows、macOS、Linux 下,封装了性能最好的网络模型 I/O 完成端口、kqueue、epoll,在多个重要产品中稳定运行。
我对这些设施当然是信任的,唯一要考虑的问题是,如果用 Java 来包装它们,不可避免要用到大量的 JNI,这样一来,维护成本大大增加,构建、调试都比较困难。而当时 netty 也很成熟了,它也封装了各个平台上的高性能网络模型。尽管 netty 内部也有 JNI,但它久经考验且外部感知不到,也节省了把 C++ 库包装为 Java 库的时间(这挺费事的,由于 C++ 多范式且特性丰富,两门语言不大容易优美地对话,另外,那些 C++ 代码时间太久了,有些地方也腐化了)。
netty 尽管也有一些坑,例如,服务端意外退出,ByteBuf 奇怪错误,连接池资源泄露,等等。不过,同事们当中有经验丰富的,基本上坑都踩过,所以问题不大。
最终的选择是 netty。同事和业界高手,我同样报以信任。
主要 API
HTTP 服务器 API 大概这个样子:
Server.createDefault()
.route("GET", ((resp, req) www.ysyl157.com-> {
//
}))
.andRoute("POST", ((resp, req) -> {
//
}))
.andRoute("PUT", ((resp, req) -> {
//
}))
.StartTLS(cert, key, 443);
或者这个样子:
@Namespace(prefix = "/model")
public class ModelNamespace {
@Router(pattern = "/{id}/predict", method = "POST")
public void predict(Context ctx, String id) {
//
}
@Router(pattern = "/{id}/info"www.dasheng178.com,www.tiaotiaoylzc.com/ method = "GET")
public void info(Response resp, Request req, String id) {
//
}
}
HTTP 客户端大概这个样子:
// 省略了错误处理
Client client = new Client();
Response resp = client.doSync(req); // 同步地
CompletableFuture<Response> respOther www.hengda157.com= client.doAsync(req); // 异步地
客户端方面,如果对方的服务器支持 HTTP/2,则运用多路复用,否则,会维护一个连接池。
翔一样的 java.net.HttpURLConnection 实在不想再用了。
实现 HTTP/2
实现 HTTP/2 协议解析,把握核心概念是关键:
数据流(stream)
消息(message)
帧(frame)
由于之前对 SPDY(HTTP/2 的前身)不熟,所以理解这些概念还是费了一些力气的,不过好在最后也弄清楚了。数据流的优先级,处理依赖和权重,帧的分割和组装,HPACK 中的霍夫曼树编码(好像又回到了学生时代),服务端推送……不知经过了多少次调试、检查、修改,最后在 Chrome 和 Firefox 上测试成功了。当然,最重要的是客户满意了,这个项目也作为我们的技术积累。
DAO 模块 & Spring 整合模块
这两个地方没什么可说的,把 Hibernate 和 MyBatis 浅浅包装了一下,再加上连接池。连接池这块,我抽象出了“策略”接口,这样,使用者如果对内置的连接池(c3p0 等)不满意,则可通过实现策略的方式自己来搞。
而整合 Spring 是为了要它的依赖注入、切面织入、事务管理等功能。
进化为 Spring Boot Starter
再后来,随着 Spring Boot 的日渐成熟完善,我们将其引入了生产环境。同时,将 Fxxxx 去掉 DAO 模块和 Spring 整合模块,专注 Web 层,改为了 Spring Boot Starter。如此一来,Fxxxx 和 Spring 全家桶整合到一起,用起来更加舒畅。
继往开来
后来,我离开了这个团队。
技术的发展却一直没有停下脚步,几乎所有的基础实施都开始支持 HTTP/2 了,如 Nginx 1.10,Tomcat 9,新版本的 Undertow。OkHttp 则提供了精美的支持 HTTP/1.1 和 HTTP/2 的客户端,而后 Java 9 也做到了这一点。
随着行业技术进步,这个项目的存在意义越来越小了。但它包含了我们技术人不畏艰难,别人没有就自己干的精神,激励着我前行。
那个执事,争先:我如何于 2015 年在 Java Web 项目中推动 HTTP/2的更多相关文章
- java web开发中的奇葩事web.xml中context-param中的注释
同事提交了代码.结果除同事之外,其他人全部编译报错.报错说web.xml中配置的一个bean 没有定义.按照报错提示,各种找,无果. 由于代码全部都是提交到svn主干,之前也没有做过备份,只能一步一步 ...
- 关于java项目与web项目中lib包的那点事
一.在java项目中如何引入外部jar包:1.在我们的java项目下新建一个lib文件夹:2.将我们需要引入的jat包复制到lib文件夹下:3.选中我们lib包下的jar,右键选择Build Path ...
- jdbc “贾琏欲执事”
“贾琏欲执事” 1.加载驱动2.获取连接3.SQL语句4.执行SQL5.释放资源 示例: public void test_insert() { String driver="oracle. ...
- Visual Studio 2015下编译zmq项目下其他项目踩进的项目引用坑
PS.在之前的一篇文章中介绍了如何用Visual Studio 2015编译zmq,在编译同解决方案中除了libzmq之外的项目例如inproc_thr时会报错误,具如下: Severity Code ...
- Visual Studio 2015 与GitLab 团队项目与管理【2】
前一篇介绍了Git服务器的搭建,我采用的是CentOS7-64位系统,git版本管理使用的是GitLab,创建管理员密码后进入页面. 创建Users,需要记住Username和邮箱,初始密码可以由管理 ...
- vue项目中遇到的那些事。
前言 有好几天没更新文章了.这段实际忙着做了一个vue的项目,从 19 天前开始,到今天刚好 20 天,独立完成. 做vue项目做这个项目一方面能为工作做一些准备,一方面也精进一下技术. 技术栈:vu ...
- 在MyEclipse(2015)中上传项目到github的步骤(很详细)
(图文)在MyEclipse(2015)中上传项目到github的步骤(很详细) git|smartGit使用详解 SmartGit使用教程
- 使用Team Explorer Everywhere (TEE) 2015 SDK获取团队项目的签入策略
TFS的代码签入策略与IDE工具紧密相关,例如Visual Studio中设置的签入策略,只会影响Visual Studio的团队资源管理器:如果需要在Eclipse的TEE中启用签入策略,你还需要在 ...
- python爬虫之爬取糗事百科并将爬取内容保存至Excel中
本篇博文为使用python爬虫爬取糗事百科content并将爬取内容存入excel中保存·. 实验环境:Windows10 代码编辑工具:pycharm 使用selenium(自动化测试工具)+p ...
随机推荐
- scala : 类型与类
scala类型系统:1) 类型与类 在Java里,一直到jdk1.5之前,我们说一个对象的类型(type),都与它的class是一一映射的,通过获取它们的class对象,比如 String.class ...
- VirtualBox主机和虚拟机互相通信
默认情况下VirtualBox虚拟机网络设置为网络地址转换,虚拟机中的地址一般是10.0.2.x,虚拟机中访问主机只需要访问默认网关地址即可,但是主机访问虚拟机就需要增加一些配置了,方法有以下几种: ...
- FTP如何通过Windows防火墙
将C:\Windows\SysWOW64\ftp.exe(64位系统)C:\Windows\system32\ftp.exe(32位系统)文件添加到防火墙的允许列表中即可. 注:“控制面板” -> ...
- rocketmq Lock failed,MQ already started -c参数
今天部署rocketmq集群时一台机器部署一个master 和slave,slave部署总是失败,通过查看日志显示下面的错误 java.lang.RuntimeException: Lock fail ...
- SVN部署与简单使用
原文发表于cu:2016-05-24 参考文档: http://www.tuicool.com/articles/Yv2iyu7 http://www.centoscn.com/CentosServe ...
- python实现lower_bound和upper_bound
由于对于二分法一直都不是很熟悉,这里就用C++中的lower_bound和upper_bound练练手.这里用python实现 lower_bound和upper_bound本质上用的就是二分法,lo ...
- arcgis--arcmap导出点的X,Y坐标
arcmap操作的
- 团队介绍&学长采访
1. 团队介绍 刘畅 博客园ID:森高Slontia 身份:PM 个人介绍: 弹丸粉 || 小说创作爱好者 || 撸猫狂魔 我绝对不知道,我一个写代码的怎么就当PM去了? 张安澜 博客园ID:Mins ...
- Bracket Sequences Concatenation Problem括号序列拼接问题(栈+map+思维)
A bracket(括号) sequence is a string containing only characters "(" and ")".A regu ...
- LCA(Tarjan算法)模板
一.查询一组的LCA Nearest Common Ancestors A rooted tree is a well-known data structure in computer science ...