最近在给Vert.x 4.x版本和3.5.0版本做监控探针的需求,应leader的要求,回顾总结一下Vert.x的一些技术点,包括但不限于和tomcat,springcloud的对比云云。
首先,谈一下做这个需求得到的一些技术上的提升和处理问题解决方法的提升还有事后诸葛亮对当时处理问题不够机智的一些反思。
1.反思:
Vert.x做为一个不温不火的一个框架,要写Vert.x的测试demo以及是用户常用的demo来说,网上资料很少,几乎很少有公司是大规模使用这个异步框架的,据内部消息而言,不少新兴的互联网造车企业是用的这个,比如:合众汽车(哪吒),上汽阿里智己汽车等是用的vert.x,当时花了很多时间去在GitHub上搜索对应的测试demo结果都无功而返,搞出来的测试demo都无法命中埋点。最绝望的时候给pinpoint的开发者发邮件,让其分享测试demo,买了Vert.x实战的书,确实这玩意儿不了解真的挺痛苦的,他的写法跟传统的springboot的controller完全不同。期间花了大量的时间去看书,看vert.x的官方文档。后面倒是在pinpoint github的提交记录上发现了 pinpoint的韩国开发同学把他们的测试demo发在了pinpoint开源上,这可真是眼瞎啊,后面在这个基础上 改造vert.x来适应 4.2.2和 3.5.0.幸好读了大量的官网资料和官网推荐的vert.x实战的书,花了大概半天的工夫把demo给完成了,有了demo 有了异步调用再去远程调试,这样难度就不再大了,后面在有如神助的情况下,完成了 4.x版本和3.5.0版本的兼容。同时还给pinpoint的同学提了bug,他们表示会在后续的版本修复我提的bug,瞬间觉得自己有点小牛逼,可以给开源的pinpoint贡献自己的代码了,哈哈哈哈,这里还留了个私心,还有个兼容的bug我照亮好久才解决掉,就没有给pinpoint的提,这样我们的竞品相对于他们的会有些优势。讲道理虽然不喜欢韩国棒子,但是人家的pinpoint写的是真的好,国内要是再多一些这样的开发就好了。
2.技术提升 Vert.x demo解析:
这里以4.2.2版本为例来聊一下Vert.x,如果读者用的是3.9.8,3.5.0的话,咋们也可以私下邮件聊下,在我不忙的时候,废话不多说,上代码:
```java
import io.vertx.core.*;
import io.vertx.core.http.*;
import io.vertx.core.net.SelfSignedCertificate;
import io.vertx.ext.web.Router;
import java.util.concurrent.TimeUnit;
public class Vertx4PluginTestStarter extends AbstractVerticle {
public static void main(String[] args) {
//创建和销毁一个Vertx,每完成一次都会做这个操作
Vertx vertx = Vertx.vertx();
vertx.deployVerticle(new Vertx4PluginTestStarter());
}
@Override
public void start(Promise<Void> startPromise) throws Exception {
//这里是建一个服务端,设置对应的参数
HttpServerOptions options = new HttpServerOptions();
options.setIdleTimeout(10000);
options.setSsl(true);
SelfSignedCertificate selfSignedCertificate = SelfSignedCertificate.create();
options.setKeyCertOptions(selfSignedCertificate.keyCertOptions());
options.setTrustOptions(selfSignedCertificate.trustOptions());
//设置路由,这里设置路由的作用是可以访问多个URL,起简化代码的作用,这个也是Vert.x的核心内容
Router router = Router.router(vertx);
router.get("/").handler(routingContext -> {
routingContext.response().end("Welcome pinpoint vert.x HTTP server test.");
});
router.get("/request").handler(routingContext -> {
request(80, "www.baidu.com", "/");
routingContext.response().setStatusCode(200).end("Request www://baidu.com:80/");
});
router.get("/request/local").handler(routingContext -> {
request(8080, "localhost", "/cn/cn/user?username=mary");
routingContext.response().end("Request http://localhost:8080/cn/cn/user?username=mary");
});
//调用www.baidu.com
router.get("/request/local/zhicc/422").handler(routingContext -> {
//建一个客户端去请求百度的地址
HttpClientOptions options1 = new HttpClientOptions().setDefaultHost("baidu.com");
// Can also set default port if you want...
HttpClient client = vertx.createHttpClient(options1);
client.request(HttpMethod.GET, "/", ar1 -> {
if (ar1.succeeded()) {
HttpClientRequest request = ar1.result();
request.send(ar2 -> {
if (ar2.succeeded()) {
HttpClientResponse response = ar2.result();
response.end();
System.out.println("Received response with status code " + response.statusCode());
}
});
}
});
//请求结束后,结束响应
routingContext.response().end("Request www.baidu.com");
});
//调用Springboot http://localhost:8080/hello
router.get("/request/local/springboot/422").handler(routingContext -> {
//HttpClientOptions options2 = new HttpClientOptions().setDefaultHost("127.0.0.1:8080");
// Can also set default port if you want...
HttpClient client = vertx.createHttpClient();
client.request(HttpMethod.GET,8089, "localhost", "/hello", ar1 -> {
if (ar1.succeeded()) {
HttpClientRequest request = ar1.result();
// Send the request and process the response
request.send(ar -> {
if (ar.succeeded()) {
HttpClientResponse response = ar.result();
System.out.println("Received response with status code " + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
}
});
routingContext.response().end("Request localhost:8080" );
});
router.get("/request/https").handler(routingContext -> {
request(443, "aliyun.com", "/");
routingContext.response().end("Request http://aliyun.com:80/");
});
router.get("/noresponse").handler(routingContext -> {
});
router.get("/close").handler(routingContext -> {
routingContext.response().close();
});
router.get("/connection/close").handler(routingContext -> {
routingContext.request().connection().close();
});
router.get("/executeBlocking").handler(routingContext -> {
executeBlocking(routingContext.request(), 1);
});
router.get("/executeBlocking/wait10s").handler(routingContext -> {
executeBlocking(routingContext.request(), 10);
});
router.get("/executeBlocking/request").handler(routingContext -> {
executeBlockingRequest(routingContext.request());
});
router.get("/runOnContext").handler(routingContext -> {
runOnContext(routingContext.request(), 1);
});
router.get("/runOnContext/wait10s").handler(routingContext -> {
runOnContext(routingContext.request(), 10);
});
router.get("/runOnContext/request").handler(routingContext -> {
runOnContextRequest(routingContext.request());
});
//这里是访问路径 localhost:18080/url(上面get的URL)
vertx.createHttpServer().requestHandler(router).listen(18080, http -> {
if (http.succeeded()) {
startPromise.complete();
System.out.println("HTTP server started on port 18080");
} else {
startPromise.fail(http.cause());
}
});
}
private void executeBlocking(HttpServerRequest request, final int waitSeconds) {
vertx.executeBlocking(new Handler<Promise<Object>>() {
@Override
public void handle(Promise<Object> objectFuture) {
sleep(waitSeconds);
request.response().end("Execute blocking.");
}
}, false, null);
}
private void executeBlockingRequest(HttpServerRequest request) {
vertx.executeBlocking(new Handler<Promise<Object>>() {
@Override
public void handle(Promise<Object> objectFuture) {
request(80, "aliyun.com", "/");
request.response().end("Execute blocking request.");
}
}, false, null);
}
private void runOnContext(HttpServerRequest request, final int waitSeconds) {
vertx.runOnContext(aVoid -> {
sleep(waitSeconds);
request.response().end("Run on context");
});
}
private void runOnContextRequest(HttpServerRequest request) {
vertx.runOnContext(aVoid -> {
request(80, "aliyun.com", "/");
request.response().end("Run on context request.");
});
}
private void sleep(int waiteSeconds) {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(waiteSeconds));
} catch (InterruptedException e) {
}
}
//这块是pinpoint那边写的,结果会报错,会包连接close
public void request(int port, String host, String uri) {
final HttpClient client = vertx.createHttpClient();
client.request(HttpMethod.GET, port, host, uri, new Handler<AsyncResult<HttpClientRequest>>() {
@Override
public void handle(AsyncResult<HttpClientRequest> asyncResult) {
System.out.println("## Result=" + asyncResult.result());
System.out.println(host+":"+port +uri);
}
});
}
//这块是我根据官方文档去修改的
public void request1( String uri) {
final HttpClient client = vertx.createHttpClient();
client.request(HttpMethod.GET, uri, ar1 -> {
if (ar1.succeeded()) {
HttpClientRequest request = ar1.result();
System.out.println(request.end());
}
});
}
}
3.对比tomcat和springcloud的优势和缺点(后续补充)
4.vert.x线程池监控后的一些思考
Vert.x的线程池分为vert.x-internal-blocking和vert.x-worker-thread这两种属于vert.x的内部线程池,顾名思义一个是内核阻塞线程池和工作线程池,还有个比较核心的但算不上线程池,叫eventLoopThreadGroup,eventLoop线程组,这个是利用netty,利用vert.x的eventLoopThreadFactory去创建线程,按照用户的需求,建立一定poolsize的线程组,达到了单线程异步非阻塞的效果。