Java 已经22岁了,依靠强大的功能、庞大的开发社区和无人能及的生态系统,长期占据世界编程语言排行榜首,成为当之无愧的业界之王。本人在大学时期被这种很有艺术性的开发语言所吸引,果断抛弃C,学习方式很简单,只看JDK API源码,直到现在都是如此。刚毕业就一直从事Java开发方面的工作,至今也有十来年了。从JSP、WebWork到Struts、JSF,从JDBC、Hibernate到TopLink、JPA。从NIO、Mina到Netty、Grizzly。很多框架都用过,研究过。后来渐渐觉得各阶段主流的框架功能很强大,设计理念也很好,但是我们大部分情况只使用其中一小部分功能,框架在实现上性能也普遍一般(除少数追求性能的框架),且适应新版JDK发布版本普及也会滞后两三年以上。慢慢地发现用了那么多整合的框架并没有比JBuilder时代的开发速度敏捷多少,很多时候在原始的Servlet、JDBC基础上做一定的封装就能满足大部分的需求。人人觉得一个优秀的框架除了保证性能和稳定性,也要注重简易性,而不是满足开发者需求的同时大量依赖包、繁琐的配置(过多的配置性的注解也是配置)和臃肿的jar刷其存在感。在长期看JDK API源码、学习开源框架设计理念和参考JavaEE规范接口设计的习惯下自然有了自己的思路和设计想法,开始学写自己的框架。于是,在2015年框架基本雏形出来并命名为Redkale,2016年正式开源。
太臃肿
如今在Java界,Tomcat、Struts2、Hibernate、MyBatis、Jetty、Spring MVC/Spring Boot/Spring XXX这些框架大家都耳熟能详,似乎不懂其中一二都不好意思说自己会Java,Spring已经成了Java的代名词,很多四五年以上的开发人员只识开源框架不识JDK,谈起这些框架的原理、优缺点,各种SOA、集群、分布式、事务、微服务概念头头是道,都可以出书了,但是写出来的代码或做出来的数据结构设计,堪比实习生。2004年发布正式版的Spring以其轻量著称,一战成名,打败了J2EE,成为Java事实上的"标准"。然而经过十几年的发展,其生态越来越庞大,功能的增多,复杂的配置,兼容性,历史包袱,使得整个框架体系越来越臃肿,还有不计其数的衍生插件,已经不比当年J2EE轻多少了。在那个JBuilder还是主流IDE的年代,建一个Web系统还是比较简单,除了Tomcat比较大点,其他都算轻量。现在很多人建一个Web项目,需要复杂的Maven建工程,一行代码还没开始写,仅仅是导入SSH/SSM这些框架就需要好几十M,还得进行一堆SSH配置,简单的日志都需要配备log4j/snf4j。可能只是简单的管理系统,必须要搞这么复杂吗? 现在一个1-2M的框架大家都认为是轻量级,MyBatis是JDBC基础上的再次封装,jar包大小近6M,但比Hibernate还是轻量些。这些只是基础框架,如果系统还需要其他功能性的框架(Lucene、Mail、Json) 会使开发包更大。同时大量的开源框架抑制了新JDK的普及,NIO出来都十五年, AIO(NIO.2)也出来六年了,如今还有很多人把NIO给贴上高性能的标签,JDK 5——Java第一个重大更新的版本在2004年发布,直到五-六年后Spring的注解方式才得到普及(Spring旧版本xml方式普及度高), Java第二个重大更新的版本JDK 8发布也有三年了,大部分人还停留在只识语法糖Lambda的程度,在JDK功能越来越强大的情况下,开发Java系统并不简化多少。相对于现在流行的NodeJs做个Web系统来说,Java就是个巨无霸。其实呢,Java无需这么复杂,真的可以很简单。
异步呢
用Java开发异步系统是件很难的事情,首先Java规范中异步接口很少,Servlet 3.0虽然支持异步,也出来好几年了,但现在直接用Servlet写代码的人已经不多,而基于Servlet的Struts、Spring Boot这些框架又没把异步太当回事,基本都是实现的Servet同步方法。其次JDBC更是迄今为止还没有异步接口,基于JDBC的Hibernate、MyBatis同样不会有异步接口。再者开源框架以异步接口为主的凤毛麟角。 所以Java程序员在写业务代码时基本都是用同步方式,特别是最耗时的数据源操作JDBC无法异步。
@WebServlet(value = {"/order/*"}, comment = "订单模块")
public class OrderServlet extends HttpBaseServlet {
@Resource
private OrderService service;
@WebMapping(url = "/order/find", comment = "查询单个订单")
@WebParam(name = "#", type = long.class, comment = "订单ID")
public void logout(HttpRequest req, HttpResponse resp) throws IOException {
long orderid = req.getRequstURILastPath(0L);
resp.finishJson(service.findOrder(orderid));
}
}
@Comment("订单服务")
public class OrderService implements Service {
@Resource
private DataSource source;
@Comment("查询单个订单")
public Order findOrder(long orderid) {
return source.find(Order.class, orderid);
}
}
以上范例是大多数Java开发者的常规写法,Servlet调用Service,Serivce调用数据库操作返回结果,都是同步操作, 而基于JDBC的开发使得最耗时的数据库操作占用了大量的线程时间,即使HTTP使用NIO、AIO(NIO.2)都收效甚微。而在NodeJs里,IO操作都是异步的。如下:
http.post("/url", {id:10}, function(request, response) {
service.getResource(request, function(result){
response.write(JSON.stringify(result));
response.end();
});
});
var mysql = require('mysql');
var pool = mysql.createPool(config);
pool.getConnection(function(err, connection) {
connection.query( 'SELECT * FROM table', function(err, rows) {
connection.end();
});
});
这也是Nodejs做些小系统比Java还快。Java在语言和IO上的性能优势弥补不了同步来带的线程消耗。
Redkale 一个全新设计的Java异步微服务框架。集HTTP、WebSocket、REST、JSON、RPC、DB操作、依赖注入等功能于一身,其 redkale-1.6.1.jar 包大小仅790K,且不依赖任何第三方包。大部分框架自身就很庞大,满足了开发者的需求同时也带来复杂的配置。如同上班只有三四里路,要买辆汽车代步,开车轻松的同时会带来车的保险保养,堵车,加油,下车库,找停车位等副作用, 其效果还不如一辆自行车来得简单高效。Redkale 返璞归真,抛弃沉重的历史包袱(javax.servlet、JPA等),基于JDK 8设计微服务架构,尽可能挖掘新JDK的优势,在保证高性能的情况下追求框架的简易性。其核心分三层结构:接入层Servlet、逻辑层Service、数据层Source。 并且三层都支持异步接口,从HTTP接收请求到Service处理逻辑再到Source拉取数据,全程都可异步,最大限度的提高CPU使用率。为适应前后台分工的开发场景,Redkale提供方便的API文档生成功能,无需编写标准的Doc文档也能让前端开发人员知道后台API接口。
Servlet
在API设计上,Redkale下足了功夫,敢于抛弃标准。其HTTP服务不再是javax.servlet——J2EE中使用最多的规范的实现,在接口形式上与NodeJs的HTTP模块很类似,易于操作。Servlet规范对于现在的应用来说过于体系化。时下移动APP、发达的前端、高性能浏览器、静动分离、REST、分布式这些因素已经让JSP、PHP、ASP这些通过后台生成页面的集中式开发框架显得不适时宜。NodeJs中的HTTP模块源码不过十几K,而Java里HTTP服务主流还是Tomcat——一个8M的重型机器。 Redkale摒弃了已经落伍的规范和功能(JSP、InputStream/OutputStream操作、Session对象、Filter等),弱化Web概念,HTTP与SNCP服务一样,只是接入层,无需做过重的设计。
其他语言的HTTP框架在处理请求后大多是以response关闭整个请求处理的方式为主,而Servlet规范却是在service方法执行后关闭,Servlet3.0规范虽然支持异步,显然与其他语言框架比,比较生涩难懂,其API使用方式还需与同步方式区别对待,且接口设计本身就会导致实现上性能不佳,而response关闭的方式天然的把同步与异步等同对待,无论是当前线程还是另开线程处理请求,都由response来结束请求处理。同样Servlet规范中WebSocket接口设计上也是过于复杂,且只是针对协议本身的实现,没有考虑大多开发者的使用场景,WebSocket本质上就像一个keepalive的Http请求,雷同HttpServlet。所以Redkale在设计WebSocket时尽量与HttpServlet形态保持一致,同时还集成了分布式功能,让开发者也可以很简单的实现多部署。
Service
Redkale所有API设计中最精简的当属RPC功能,RPC没有直接调用的API,其功能依附在Service。RPC或类似功能的框架在Java里一直是比较重量级的,从古老的Corba、RMI到后来的EJB、WebService,还有其他很多RPC开源框架,都有着复杂的配置和大量API学习成本,有些还需要区分客户端和服务端(如WebService)。 Service不仅只是个逻辑层的规范定义,还集成了很强大的RPC和异步调用功能,远程模式的Service就是RPC功能,系统在依赖注入过程中创建Service时通过基本的IP配置自动识别是创建本地模式的Service还是远程模式的Service,远程模式的Service使用的就是RPC,但在代码层Service的调用本地模式与远程模式完全一样。更神奇的是,带有异步回调函数AsyncHandler 的Service方法同样能执行远程模式。这种RPC的简易性是其他框架都无可匹敌的。REST风格的Service在接口设计上尽量减少注解性的配置,同时保留灵活性,减少HttpServlet的编写。
Source
除了开发类似数据库管理工具,大部分情况下开发者只用基本的增删改查操作,Hibernate、MyBatis这些框架对于对象新增/更新/删除和主键查找对象等接口定义都比较简单,但是带有过滤条件的操作,就变得难用。MyBatis需要配各种SQL,大量if else,Hibernate需要写HQL或SQL,JPA规范需要写JPQL或使用Criteria功能。过滤查询、翻页查询、过滤修改、局部修改、过滤删除都是很常见的操作,而主流ORM框架对这类操作进行简化程度很有限。Redkale 结合常规的使用场景,以JavaBean的方式提供过滤功能,无需编写SQL或类似SQL的配置,使过滤性的操作(删改查)API变得异常简单。DataSource另一个亮点是分表分库操作与单表操作的API一样。同时每个操作都提供成异步接口,但是又限于JDBC的同步性,DataSource的默认实现提供的异步接口也只是JDBC的同步操作,若开发者追求性能极致的话,可以使用AIO(NIO.2)技术编写不基于JDBC的DataSource异步实现。 DataSource提供的缓存功能还能保持进程间的同步(得利于RPC)。
思维
Redkale 追求大道至简,不仅提供高性能的功能和简易的API,还带来不同的设计思维。作为一名有追求的开发者,不能只停留在API层面,更多的是需要掌握设计能力,一个好的设计方案往往能少写很多代码。Java里很多规范和框架就是考虑太过全面,为了迎合各种良莠不齐的想法和设计。比如HTTP服务,只是系统的一个接入层,有必须设计ServletConfig、ServletContextListener、Filter、HttpSessionListener等这么多API吗,开发者在系统初期养成定义全局的BaseHttpServlet这些基类的习惯就可以控制很多东西,也无需去使用什么拦截器或AOP功能。提交表单前先将表单数据转换成JSON字符串传给后台,后台的接口既可用于Web也可用于APP,非要按原始表单提交,那只能使用Struts这类笨重的框架了。再如DB操作,开发者设计好的数据结构可以把关系型数据库当NoSQL数据库操作,会发现对JDBC做一定的封装就可以基本杜绝写SQL。非要写存储过程、关联五六张表进行复杂查询,再好的DB框架都满足不了你。再如Date,Date对象的本质是long值,很多人习惯性的数据库就用Date类型,这样会增加很多麻烦,增加数据库的通用性难度,JSON还需要提供各种DateFormat,如果使用long类型,时间只交给页面去format就简单很多,long的性能也更好。说了这么多,只是想表达一个观点,开发时摆脱传统思维的桎栲,换个思路去思考,很多东西会变得很简单。
想了解更多关于Redkale的资料, 请访问Redkale官网: :http://redkale.org。