故事的开头要从博主两年前做的一个需求开始. 当时我在公司的Android项目组做开发, app要加一个免流量分享应用的功能. 应用场景是这样: 我手机上装了一个app, 现在要传给你, 不耗费双方的上网流量. 怎么做呢? 过程是这样:
当时从技术上来拆分, 主要有以下三点:
http这一块Android的客户端支持是很完善的, 像apache的HttpClient, java自带的UrlConnection都可以用, 但对于服务器这一块, 还真没接触过, 毕竟 像Tomcat, nginx这种web服务器一般来说都部署在专用服务器主机上的. 这该咋办呢?
基于当时的认知情况, 博主采用了一种最原始的方法: 基于Java的原生socket, 手动写一个简单的web服务器. 大体实现如下:
1.绑定tcp socket到8080端口, 接受连接请求
2. 收到连接请求后, 读取客户端发来的数据
3. 按行解析这些数据, 分析Request-Line, 得到访问的路径, 如果是”/”, 则读取index.html文件, 写入连接socket. 当然了, 在写文件数据前, 需要按照http规范发送Response-Header, 像Content-Length, Content-Type这些字段; 如果是”/download”, 则按照文件的格式发送Response-Header, 跟上apk文件的数据; 其他路径就直接返回404错误.
最后按这种方式实现了目标功能. 但是一眼看来这样实现是有缺点的, 稍微复杂一点的功能都要手动在这个基础上添加, 而很多功能其实都是在HTTP规范里面定义好的, 只要是一个规范的http服务器, 都会实现这些功能, 手动写服务器其实是重复造轮子的动作. 当时在想, 要是有一个可以方便嵌入到手机的web服务器该多好啊!
后来, 由于博主的兴趣在服务端, 正好公司有这个契机, 我就转去做服务器了. Android端也有一部分工作, 主要是给业务团队提供SDK, 封装一部分JNI.
又过了一段时间, 博主遇到一个需求, 手机客户端的日志, 存储到后台做分析. 这里又遇到http服务器了, 当然这次运行的目标机器是服务器, 可选择的就很多了, 当时的决策如下:
有的同学一定会疑问怎么没使用Netty呢, 至少我身边的人对Netty提到的机会比jetty是高很多的. 老实说, 如果当时没有误以为Jetty底层就是Netty的话, 我当时可能会考虑使用Netty来做开发. 不过到现在这个时候来说, 我觉得一点也不后悔. 主要有以下几点:
说了这么多, 其实是因为博主和大多数工程师一样, 都有些强迫症情节, 用东西都追求最好的, 性能要最高的. 这在很多时候其实是不好的, 高性能的东西, 可能用起来不顺手, 或者开发和部署需要比较多的时间, 或者发挥高性能的前提是要对它有足够的认知. 作为工程师的天职, 是为解决生活中的问题而存在的. 一味地追求高端, 只会迷失在茫茫多的组件当中. 而从这么多功能相近的组件同时存在也可以看出, 每个组件都有自己的立足之地, 一定是因为它很好的满足了一部分人的需求. 选择合适自己的, 了解它, 改进它!!!
Jetty是Eclipse基金会下面的一个开源项目, 是一个Web服务器和Servlet容器, 同时支持HTTP/2, WebSocket, OSGi, JMX, JNDI, JAAS功能. Jetty可以作为web服务器非常容易的嵌入到应用代码中, 包括Android的app.
Jetty除了嵌入到应用代码中外, 也可以像Tomcat那样独立运行, 通过部署WAR包的方式提供服务. 理论上来说, Tomcat下面运行的WAR包可以直接在Jetty下运行, 这一点我没有验证过. 有需要的同学可以自行验证. 接下来的博客中主要介绍嵌入运行方式.
接下来通过一个简单的例子来看看Jetty是如何开发的.
package com.lqp.test;
import org.apache.logging.log4j.LogManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ResourceHandler;
public class TestJettyServer {
public static void main(String[] args) {
Server server = new Server(8080);
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
ContextHandler resHanler = new ContextHandler();
resHanler.setHandler(resource_handler);
resHanler.setResourceBase(".");
resHanler.setContextPath("/");
server.setHandler(resHanler);
try {
server.start();
} catch (Exception e) {
LogManager.getLogger().catching(e);
}
}
}
启动一个JettyWeb服务器就是这么简单. 启动后, 就可以在浏览器里浏览和查看当前目录下的文件了. 效果如下图:
Handler是Jetty服务器的业务功能实现地, 如果要扩展web服务器的功能, 只需要添加自己实现的handler就行, 还是很容易理解的.
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-serverartifactId>
<version>9.4.2.v20170220version>
dependency>
<dependency>
<groupId>org.eclipse.jettygroupId>
<artifactId>jetty-servletartifactId>
<version>9.4.2.v20170220version>
dependency>
后面的系列中, 主要围绕9.4.2.v20170220版本的代码来做分析.