TIP:相应的源代码在step-1中(https://github.com/vert-x3/vertx-guide-for-java-devs)java
咱们开始第一步,用vert.x编写一个简单可行的wiki应用,而在下一步的迭代中,将会引入更多好的代码库,以及适当的测试,咱们能够看到用Vert.x构建一个原型是一个简单实在的目标。git
在这个阶段,wiki应用将使用服务端渲染HTML页面,数据库持久层用的JDBC链接,咱们将使用下面的类库来完成要作的。github
1.Vert.x web做为Vert.x核心库用来建立HTTP服务,可是没有提供优雅的apis处理路由,请求处理。web
2.Vert.x JDBC client用来提供在JDBC基础的异步API。sql
3.Apache FreeMarker是用来渲染服务器端页面的一个简单模板引擎。数据库
4.Txtmark用来渲染Markdown到HTML,容许用Markdown编辑wiki页面。编程
初始化一个maven项目api
这个guide选择用Apache Maven做为构建工具,主要是由于它很好地集成了主要的开发环境,你也可使用其余的构建工具,好比Gradle。缓存
Vert.x社区提供了模板项目结构(https://github.com/vert-x3/vertx-maven-starter ),你能够选择合适的clone下来。若是你偏向于使用Git做为版本控制,最快的选择是clone那个repository,而后删除其中的 .git/文件夹,而后在其中建立一个新的Git仓库安全
git clone https://github.com/vert-x3/vertx-maven-starter.git vertx-wiki cd vertx-wiki rm -rf .git git init
这个项目提供一个简单的verticle,以及一个单元测试。你能够安全地删除wiki项目下src/文件夹下的java文件,可是在须要项目构建,还有能够跑起来。
mvn package exec:java
你能够注意到maven项目中pom.xml作了两件集成的事:
1.使用Maven Shade Plugin来打包一个拥有所有依赖的jar包,带有 -fat.jar后缀,因此咱们能够称之“fat jar”
2.使用Exec Maven Plugin提供exec:java命令,这样能够经过vert.x的io.vertx.core.Launcher类来启动项目,这实际上算是分布式Vert.x的命令行工具。
最后,你会发现的redeploy.sh和redeploy.bat脚本的存在,你能够选择使用自动编译和部署的更改的代码。注意这么作,须要肯定脚本中的VERTICLE变量和使用的主verticle匹配。
NOTE:
另外,Fabric8项目有一个Vert.x Maven plugin,有初始化,构建,打包和运行vert.x的命令。
也能够相似的经过clone git仓库来建立一个项目
mkdir vertx-wiki cd vertx-wiki mvn io.fabric8:vertx-maven-plugin:1.0.7:setup -DvertxVersion=3.5.0 git init
添加须要的依赖
在pom.xml中添加web处理和渲染的依赖:
<dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web</artifactId> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-templ-freemarker</artifactId> </dependency> <dependency> <groupId>com.github.rjeschke</groupId> <artifactId>txtmark</artifactId> <version>0.13</version> </dependency>
TIP
顾名思义,vertx-web-templ-freemarker就和你想的同样,Vert.x web提供了对流行的模板引擎的支持:Handlebars, Jade, MVEL, Pebble, Thymeleaf。
第二波要添加JDBC须要的依赖:
<dependency> <groupId>io.vertx</groupId> <artifactId>vertx-jdbc-client</artifactId> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.4</version> </dependency>
Vert.x JDBC client库提供了任何兼容的JDBC数据库,固然咱们在路径中须要设置JDBC驱动。
HSQLDB是一个用java编写的内嵌数据库,当使用内嵌数据库避免第三方数据库依赖是比较流行的选择,在单元测试和集成测试中提供内存数据库也很流行。
NOTE
vert.x也直接提供了MySQL and PostgreSQL client 库。
固然你也可使用常规的Vext.x JDBC client链接Mysql或者PostgreSQL数据库,可是那两个类库提供更好的性能处理服务器网络协议,比起阻塞的JDBC APIs。
NOTE
vert.x也提供了类库处理流行的非关系型数据库MongoDB和Redis。强大的社区提供其余存储系统的支持,好比Apache Cassandra, OrientDB or ElasticSearch。
解剖verticle
咱们的wiki项目的verticle包含一个io.vertx.guides.wiki.MainVerticle类,这个类继承了io.vertx.core.AbstractVerticle,基本的verticles提供了以下几点:
1.重写生命周期的 start和stop方法
2.一个protected字段指向verticle在Vert.x中的部署的环境
3.一个能够访问verticle配置文件和传递外部配置文件的访问器。
咱们开始咱们的verticle,经过下面覆盖 start方法
public class MainVerticle extends AbstractVerticle { @Override public void start(Future<Void> startFuture) throws Exception { startFuture.complete(); } }
这里的start和stop方法有两种形式,一种没有参数,一种有Future参数。没有参数的方法意味着verticle初始化和house-keeping在没有抛出异常的状况都会成功;带有Future参数的方法提供了更细粒度的方法,指出操做成功仍是失败。事实上,一些初始化或者清除的代码须要异步的操做,所以须要future对象来匹配异步结果。
vert.x的Future对象和回调
Vert.x的Futures不是JDK的futures:他们能够以非阻塞的方式查询和调用,他们能够应用于异步任务调度中,尤为在verticles 发布和检查他们是否发布成功。
Vert.x的核心APIs是在回调和异步事件的通知的基础上写的。经验丰富的开发者很天然地会认为,这样打开了“callback hell”的大门,当有屡次嵌套的时候,那时候代码真是使人难以理解,经过下面这个虚构的代码来讲明:
foo.a(1, res1 -> { if (res1.succeeded()) { bar.b("abc", 1, res2 -> { if (res.succeeded()) { baz.c(res3 -> { dosomething(res1, res2, res3, res4 -> { // (...) }); }); } }); } });
而核心APIs的设计要高瞻远瞩,当回调的使用容许不一样的抽象调用就会变得有趣,Vert.x是一个大的开放的项目,回调能够用不一样的模块实现,这样更好地应对异步编程:响应式的拓展,使用字节码等。
由于Vert.x的APIs是以回调为主的,这个guide在第一节使用回调来使读者熟悉Vert.x的核心概念。对于使用异步是很是简单的,能够在不一样的章节的异步代码之间连成一线,在示例代码中,可是有些回调并非很好理解。因此咱们介绍RxJava来支持用异步代码更好的处理事件流。
Wiki verticle初始化阶段
为了让咱们的wiki运行起来,咱们须要执行2个阶段的初始化:
1.咱们须要创建一个JDBC链接,也要肯定数据库运行是正常的。
2.咱们须要为咱们的web应用启动一个HTTP服务。
每个阶段均可能失败(eg.,HTTP服务TCP端口已经占用),这样应用就不会运行,他们须要链接数据库。
为了使代码变得简洁,咱们将在每一层定义一个方法,咱们在每个方法返回的时候,采用返回一个Future的形式,不管方法是成功仍是失败:
private Future<Void> prepareDatabase() { Future<Void> future = Future.future(); // (...) return future; } private Future<Void> startHttpServer() { Future<Void> future = Future.future(); // (...) return future; }
经过每个方法返回一个Future对象,那么就能够组成了Strat方法的实现:
@Override public void start(Future<Void> startFuture) throws Exception { Future<Void> steps = prepareDatabase().compose(v -> startHttpServer()); steps.setHandler(startFuture.completer()); }
当prepareDatabase的Future返回成功,而后在执行startHttpServer方法返回成功后,steps后续方法继续执行。若是prepareDatabase方法返回失败,那么startHttpServer将不执行,而后steps的Future将是在一个failed的状态,而且将抛出带有那个异常信息的异常。
最后steps完成使命:setHandler定义一个Handler在完成的时候调用。在咱们的例子里,咱们就想简单地完成steps的startFuture,而后用complete方法操做一个Handler,下面是实现:
Future<Void> steps = prepareDatabase().compose(v -> startHttpServer()); steps.setHandler(ar -> { (1) if (ar.succeeded()) { startFuture.complete(); } else { startFuture.fail(ar.cause()); } });
数据库初始化:
wiki数据库包含一个简单的pages表,有如下字段:
典型的数据库操做增删改查,在开始以前,咱们理解一下在MainVerticle类中的静态字段。注意到这些字段写了HSQLDB数据库理解的SQL语句,可是可能其余的关系型数据库会补能执行。
private static final String SQL_CREATE_PAGES_TABLE = "create table if not exists Pages (Id integer identity primary key, Name varchar(255) unique, Content clob)"; private static final String SQL_GET_PAGE = "select Id, Content from Pages where Name = ?"; (1) private static final String SQL_CREATE_PAGE = "insert into Pages values (NULL, ?, ?)"; private static final String SQL_SAVE_PAGE = "update Pages set Content = ? where Id = ?"; private static final String SQL_ALL_PAGES = "select Name from Pages"; private static final String SQL_DELETE_PAGE = "delete from Pages where Id = ?";
代码中的verticle会保持一个JDBCClient的引用(io.vertx.ext.jdbc包)获取数据库链接,在MainVerticle中有一个属性记录日志(org.slf4j)。
而后咱们看看prepareDatabase方法的实现,有一个数据库链接,而后执行数据库操做建立一个Pages表(若是不存在的状况下)
1.createShared建立一个共享的vertx实例verticles链接,或许是个不错的主义。
2.配置JDBC的URL。
3.配置数据库链接的drive_class。
4.max_pool_size用来配置数据并发链接数,咱们在这里选择30,这里只是一个随意的选择。
5.获取数据库链接,这是一个异步的操做,返回AsyncResult<SQLConnection>,因此须要测试数据库链接是否真的被创建。
6.若是未能获取数据库链接,异步结果AsyncResult将返回一个形成这个缘由的异常。
7.SQLConnection是一个AsyResult返回成功后的结果,咱们可使用她来执行SQL语句。
8.在检查SQL查询结果是否成功以前,咱们要释放链接,不然链接池会被耗尽。
9.完成Future对象的调用成功信息。
TIP:
Vert.x项目支持的SQL数据库模型在SQL语句上没有更多的支持,而是将重点放在异步访问数据库上,然而,这并不限制使用社区中更前卫的数据库模块。咱们也能够去关注为vert.x提供支持的jOOq generator fo,或者 POJO mapper。
关于日志:
前面说到记录日志,咱们选择了SFL4J库,vert.x在日志方面不是一个顽固,基本能够支持大量流行的日志类库方案。咱们选择了SLF4J是由于java生态里面流行的日志系统和比较统一的库。
咱们也推荐使用Logback做为日志的实现,集成SLF4J和Logbck能够经过添加两个依赖实现,或者仅仅使用logback-classic引入依赖(事实上他们来自容一个做者)
默认SLF4J会在控制台输出大量的Vert.x, Netty, C3PO 和wiki应用日志,咱们能够经过增长一个配置减小冗余的内容(src/main/resources/logback.xml),更多的细节能够看 https://logback.qos.ch/
<configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <logger name="com.mchange.v2" level="warn"/> <logger name="io.netty" level="warn"/> <logger name="io.vertx" level="info"/> <logger name="io.vertx.guides.wiki" level="debug"/> <root level="debug"> <appender-ref ref="STDOUT"/> </root> </configuration>
内嵌的HSQLDB与日志并非集成的很好,默认它会使用调用系统的日志,咱们能够经过设置-Dhsqldb.reconfig_logging=false使JVM在运行程序时不调用。
HTTP服务初始化:
vert.x-web项目能够容易得定义对HTTP请求进行路由,事实上,Vert.x核心APIs语序启动HTTP服务而且监听创建的链接,可是没有提供其余的工具,不过由不一样的根据请求的URL或者处理请求的handlers。router的角色就是把请求分派给不一样的处理进程。
初始化过程包括创建一个equest router,和启动一个HTTP服务。
1.Vert.x context提供方法建立HTTP servers, clients, TCP/UDP servers and clients等。
2.构建Router(vertx-web: io.vertx.ext.web.Router)
3.Routers拥有本身的handler,能够经过URL和/或HTTP方法定义。短程序java lambda是一个选择,可是对于更详细的处理程序来讲,引用私有方法是一个好主意。注意,URL能够是参数化的,/wiki/:page匹配像/wiki/Hello的页面,在这种状况下,page表明的参数就是Hello。
4.全部HTTP POST请求都经过第一个handler(io.vertx.ext.web.handler.BodyHandler),这个处理程序会解析HTTP请求(好比表单提交),能够用来处理Vert.xd的缓存对象。
5.router对象能够用做HTTP服务器处理程序,而后将其分派给如上所定义的其余处理程序。
6.启动一个HTTP服务是一个异步操做,须要检测AsyncResult<HttpServer>返回的结果是否成功,顺便说一下服务器启动占用TCP的8080接口。
原文连接:http://vertx.io/docs/guide-for-java-devs/
未完待续,好累,先休息了!