Vertx http 服务器线程已被阻塞 xxxx 毫秒,时间限制为 2000
Vertx http server Thread has been blocked for xxxx ms, time limit is 2000
我已经使用 编写了一个大型 HTTP 服务器,但是当并发请求数增加时出现此错误
WARNING: Thread Thread[vert.x-eventloop-thread-1,5,main] has been blocked for 8458 ms, time limit is 1000
io.vertx.core.VertxException:线程阻塞
这是我的完整代码:
public class MyVertxServer {
public Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100));
private HttpServer server = vertx.createHttpServer();
private Router router = Router.router(vertx);
public void bind(int port){
server.requestHandler(router::accept).listen(port);
}
public void createContext(String path,MyHttpHandler handler){
if(!path.endsWith("/")){
path += "/";
}
path+="*";
router.route(path).handler(new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
handler.Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
以及我如何称呼它:
ver.createContext("/getRegisterManager",new ProfilesManager.RegisterHandler());
ver.createContext("/getLoginManager", new ProfilesManager.LoginHandler());
ver.createContext("/getMapcomCreator",new ItemsManager.MapcomCreator());
ver.createContext("/getImagesManager", new ItemsManager.ImagesHandler());
ver.bind(PORT);
我怎么没发现 eventbus() 对于处理 send/receive 文件的 http 服务器有用,因为你需要在消息中发送 RoutingContext 是不可能的。
你能给我指明正确的方向吗?谢谢
添加了一些处理程序代码:
class ProfileGetter implements MyHttpHandler{
@Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
String username = utils.Decode(ctx.request().headers().get("username"));
String lang = utils.Decode(ctx.request().headers().get("lang"));
display("profile requested : "+username);
Profile profile = ProfileManager.FindProfile(username,lang);
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
}
这里 ProfileManager.FindProfile(username,lang) 在同一个线程 运行 上做一个很长的数据库作业
...
基本上我所有的进程都发生在主线程上,因为如果我使用执行器,我会在 Vertx 中得到奇怪的异常和空指针,让我觉得 Vertx 中的请求处理器是并行的
考虑到问题中的代码量很少,让我们同意问题所在:
Profile profile = ProfileManager.FindProfile(username,lang);
假设这是在内部进行一些阻塞 JDBC 调用,这是 Vert.x 中的反模式,您可以通过多种方式解决此问题。
假设您可以完全重构 ProfileManager
class 哪个 IMO 是最好的然后您可以将其更新为反应式,因此您的代码将像:
ProfileManager.FindProfile(username,lang, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});
现在在幕后发生的事情是您的 JDBC 调用不会阻塞(这很棘手,因为 JDBC 本质上是阻塞的)。因此,要解决此问题,您很幸运可以使用 MySQL 或 Postgres,然后您可以针对 async-client if you're stuck with other RDBMS servers then you need to use the jdbc-client 编写 JDBC 代码,后者又会使用线程池来卸载工作事件循环线程。
现在假设您无法更改 ProfileManager
代码,那么您仍然可以通过将代码包装在 executeBlocking
块中来将其卸载到线程池:
vertx.executeBlocking(future -> {
Profile profile = ProfileManager.FindProfile(username,lang);
future.complete(profile);
}, false, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});
我已经使用 编写了一个大型 HTTP 服务器,但是当并发请求数增加时出现此错误
WARNING: Thread Thread[vert.x-eventloop-thread-1,5,main] has been blocked for 8458 ms, time limit is 1000
io.vertx.core.VertxException:线程阻塞
这是我的完整代码:
public class MyVertxServer {
public Vertx vertx = Vertx.vertx(new VertxOptions().setWorkerPoolSize(100));
private HttpServer server = vertx.createHttpServer();
private Router router = Router.router(vertx);
public void bind(int port){
server.requestHandler(router::accept).listen(port);
}
public void createContext(String path,MyHttpHandler handler){
if(!path.endsWith("/")){
path += "/";
}
path+="*";
router.route(path).handler(new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext ctx) {
String[] handlerID = ctx.request().uri().split(ctx.currentRoute().getPath());
String suffix = handlerID.length > 1 ? handlerID[1] : null;
handler.Handle(ctx, new VertxUtils(), suffix);
}
});
}
}
以及我如何称呼它:
ver.createContext("/getRegisterManager",new ProfilesManager.RegisterHandler());
ver.createContext("/getLoginManager", new ProfilesManager.LoginHandler());
ver.createContext("/getMapcomCreator",new ItemsManager.MapcomCreator());
ver.createContext("/getImagesManager", new ItemsManager.ImagesHandler());
ver.bind(PORT);
我怎么没发现 eventbus() 对于处理 send/receive 文件的 http 服务器有用,因为你需要在消息中发送 RoutingContext 是不可能的。
你能给我指明正确的方向吗?谢谢
添加了一些处理程序代码:
class ProfileGetter implements MyHttpHandler{
@Override
public void Handle(RoutingContext ctx, VertxUtils utils, String suffix) {
String username = utils.Decode(ctx.request().headers().get("username"));
String lang = utils.Decode(ctx.request().headers().get("lang"));
display("profile requested : "+username);
Profile profile = ProfileManager.FindProfile(username,lang);
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
}
这里 ProfileManager.FindProfile(username,lang) 在同一个线程 运行 上做一个很长的数据库作业
...
基本上我所有的进程都发生在主线程上,因为如果我使用执行器,我会在 Vertx 中得到奇怪的异常和空指针,让我觉得 Vertx 中的请求处理器是并行的
考虑到问题中的代码量很少,让我们同意问题所在:
Profile profile = ProfileManager.FindProfile(username,lang);
假设这是在内部进行一些阻塞 JDBC 调用,这是 Vert.x 中的反模式,您可以通过多种方式解决此问题。
假设您可以完全重构 ProfileManager
class 哪个 IMO 是最好的然后您可以将其更新为反应式,因此您的代码将像:
ProfileManager.FindProfile(username,lang, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});
现在在幕后发生的事情是您的 JDBC 调用不会阻塞(这很棘手,因为 JDBC 本质上是阻塞的)。因此,要解决此问题,您很幸运可以使用 MySQL 或 Postgres,然后您可以针对 async-client if you're stuck with other RDBMS servers then you need to use the jdbc-client 编写 JDBC 代码,后者又会使用线程池来卸载工作事件循环线程。
现在假设您无法更改 ProfileManager
代码,那么您仍然可以通过将代码包装在 executeBlocking
块中来将其卸载到线程池:
vertx.executeBlocking(future -> {
Profile profile = ProfileManager.FindProfile(username,lang);
future.complete(profile);
}, false, res -> {
if (res.failed()) {
// handle error, sent 500 back, etc...
} else {
Profile profile = res.result();
if(profile == null){
ctx.request().response().putHeader("available","false");
utils.sendResponseAndEnd(ctx.response(),400);
return;
}else{
ctx.request().response().putHeader("available","true");
utils.writeStringAndEnd(ctx, new Gson().toJson(profile));
}
}
});