Vert.x SQLConnection:“路由中出现意外异常”,“等待 30000(毫秒)回复后超时”

Posted

技术标签:

【中文标题】Vert.x SQLConnection:“路由中出现意外异常”,“等待 30000(毫秒)回复后超时”【英文标题】:Vert.x SQLConnection: "Unexpected exception in route", "Timed out after waiting 30000(ms) for a reply" 【发布时间】:2019-06-01 13:59:56 【问题描述】:

我将 Vert.x 与 PostgreSQL 结合使用。

目前我正在尝试向数据库发出请求,但由于某种原因,尽管从客户端收到了请求,但它并没有得到满足。

我看到的错误如下所示:

奇怪的是,如果我关闭服务器并重新启动它,请求就会得到满足。

这让我觉得它与数据库“挂起”的连接之一有关,并阻止了这个请求。

我可以设想的另一种可能性是路线有些混乱,可能吗?

路线如下所示:

Router router = Router.router(vertx);
router.post().handler(BodyHandler.create());
router.post("/create").handler(this::createHandler);
router.post("/login").handler(this::loginHandler);
router.post("/account").handler(this::accountHandler);
router.post("/invitation").handler(this::invitationHandler);
router.post("/initiate").handler(this::initiateHandler);

我有一个理论认为问题可能与向数据库发出多个请求的复合方法有关,因为执行此方法后总是发生故障:

private void account(Message<JsonObject> message) 
    JsonArray username = new JsonArray().add(message.body().getString("username"));

    JsonObject response = new JsonObject();
    response.put("response", "account");

    friends(friendsResult -> 
        if (friendsResult.succeeded()) 
            response.put("friends", friendsResult.result());

            games(username, gamesResult -> 
                if (gamesResult.succeeded()) 
                    response.put("games", gamesResult.result());

                    invitations(username, invitationsResult -> 
                        if (invitationsResult.succeeded()) 
                            response.put("invitations", invitationsResult.result());

                            System.out.println("ACCOUNT RESPONSE: " + response.encodePrettily());

                            /* * */
                            message.reply(response);
                         else 
                            System.out.println("FAILED IN <<INVITATIONS RESULT>>...");
                        
                    );
                 else 
                    System.out.println("FAILED IN <<GAMES RESULT>>...");
                
            );
         else 
            System.out.println("FAILED IN <<FRIENDS RESULT>>...");
        
    );

正如我上面所说的,它有三个“辅助”方法,在这里它们是......

一个:

private void friends(Handler<AsyncResult<List<String>>> handler)  // TODO: Replace w/ concept of <<friends>>...
    List<String> friends = new ArrayList<>();
    dbClient.queryStream(
            sqlQueries.get(SqlQuery.FETCH_FRIENDS),
            asyncResult -> 
                if (asyncResult.succeeded()) 
                    asyncResult.result().handler(row -> friends.add(row.getString(0)));
                
                handler.handle(Future.succeededFuture(friends));
            );
    dbClient.close();

二:

private void invitations(JsonArray params, Handler<AsyncResult<JsonObject>> handler) 
    JsonObject invitations = new JsonObject();
    params.add(false);
    JsonArray outboundArray = new JsonArray();
    dbClient.queryStreamWithParams(
            sqlQueries.get(SqlQuery.OUTBOUND_INVITATIONS),
            params,
            asyncResult0 -> 
                if (asyncResult0.succeeded()) 
                    asyncResult0.result().handler(row -> 
                        JsonObject invitation = new JsonObject();
                        invitation.put("identifier", row.getString(0));
                        invitation.put("user1", row.getString(1));
                        outboundArray.add(invitation);
                    );
                
                invitations.put("outbound", outboundArray);

                JsonArray inboundArray = new JsonArray();
                dbClient.queryStreamWithParams(
                        sqlQueries.get(SqlQuery.INBOUND_INVITATIONS),
                        params,
                        asyncResult -> 
                            if (asyncResult.succeeded()) 
                                asyncResult.result().handler(row -> 
                                    JsonObject invitation = new JsonObject();
                                    invitation.put("identifier", row.getString(0));
                                    invitation.put("user0", row.getString(1));
                                    inboundArray.add(invitation);
                                );
                            
                            invitations.put("inbound", inboundArray);

                            handler.handle(Future.succeededFuture(invitations));
                        );
            );

三:

private void games(JsonArray params, Handler<AsyncResult<JsonArray>> handler) 
    JsonArray games = new JsonArray();
    dbClient.queryStreamWithParams(
            sqlQueries.get(SqlQuery.FETCH_GAMES_0),
            params,
            asyncResult0 -> 
                if (asyncResult0.succeeded()) 
                    asyncResult0.result().handler(row -> 
                        JsonObject game = new JsonObject();
                        game.put("user0", params.getString(0));
                        game.put("user1", row.getString(1));
                        game.put("identifier", row.getString(0));
                        games.add(game);
                    );
                
                dbClient.queryStreamWithParams(
                        sqlQueries.get(SqlQuery.FETCH_GAMES_1),
                        params,
                        asyncResult1 -> 
                            if (asyncResult1.succeeded()) 
                                asyncResult1.result().handler(row -> 
                                    JsonObject game = new JsonObject();
                                    game.put("user0", row.getString(1));
                                    game.put("user1", params.getString(0));
                                    game.put("identifier", row.getString(0));
                                    games.add(game);
                                );
                            
                            handler.handle(Future.succeededFuture(games));
                        );
            );

这是实际失败的请求:

private void gamestateRequest(Message<JsonObject> message) 
    JsonArray identifier = new JsonArray().add(message.body().getString("identifier"));

    dbClient.queryWithParams(
            sqlQueries.get(SqlQuery.GAMESTATE_REQUEST),
            identifier,
            asyncResult -> 
                if (asyncResult.succeeded()) 
                    ResultSet resultSet = asyncResult.result();

                    JsonObject response = new JsonObject();
                    String user0 = resultSet.getResults().get(0).getString(0);
                    String user1 = resultSet.getResults().get(0).getString(1);
                    String gamestate = resultSet.getResults().get(0).getString(2);

                    response.put("response", "gamestate");
                    response.put("user0", user0);
                    response.put("user1", user1);
                    response.put("gamestate", gamestate);

                    System.out.println("GAMESTATE REQUEST RESPONSE: " + response.encodePrettily());

                    message.reply(response);
                 else 

                    System.out.println("GAMESTATE REQUEST FAIL");

                    reportQueryError(message, asyncResult.cause());
                
            );
    dbClient.close();

每条路由都与下面的处理方法相关联,如下所示:

private void handler(RoutingContext context, String headerValue) 
    LOGGER.info("RECEIVED CONTEXT: " + context.getBodyAsString());

    JsonObject data = new JsonObject(context.getBodyAsString());
    DeliveryOptions options = new DeliveryOptions().addHeader("action", headerValue);
    vertx.eventBus().send(dbQueue, data, options, reply -> 
        if (reply.succeeded()) 
            context.response()
                    .putHeader("content-type", "text/html")
                    .end(reply.result().body().toString());
         else 

            System.out.println("FAIL IN <<handler>>...");

            context.fail(reply.cause());
        
    );

也许这与 EventBus 有关?从透视角度来看,它的配置如下:

@Override
public void start(Future<Void> future) throws Exception 
    loadSqlQueries(); // NOTE: This method call uses blocking APIs, but data is small...

    dbClient = PostgreSQLClient.createShared(vertx, new JsonObject()
            .put("username", "s.matthew.english")
            .put("password", "")
            .put("database", "s.matthew.english")
            .put("url", config().getString(CONFIG_JDBC_URL, "jdbc:postgresql://localhost:5432/wiki"))
            .put("driver_class", config().getString(CONFIG_JDBC_DRIVER_CLASS, "org.postgresql.Driver"))
            .put("max_pool_size", config().getInteger(CONFIG_JDBC_MAX_POOL_SIZE, 30)));

    dbClient.getConnection(connectionResult -> 
        if (connectionResult.succeeded()) 
            SQLConnection connection = connectionResult.result();
            connection.execute(sqlQueries.get(SqlQuery.CREATE_USERS_TABLE), createUsersTableResult -> 

                if (createUsersTableResult.succeeded()) 
                    connection.execute(sqlQueries.get(SqlQuery.CREATE_GAMES_TABLE), createGamesTableResult -> 
                        connection.close();

                        if (createGamesTableResult.succeeded()) 
                            vertx.eventBus().consumer(config().getString(CONFIG_QUEUE, "db.queue"), this::onMessage);
                            future.complete();
                         else 
                            LOGGER.error("Database preparation error", createGamesTableResult.cause());
                            future.fail(createGamesTableResult.cause());
                        
                    );
                 else 
                    LOGGER.error("Database preparation error", createUsersTableResult.cause());
                    future.fail(createUsersTableResult.cause());
                
            );
         else 
            LOGGER.error("Could not open a database connection", connectionResult.cause());
            future.fail(connectionResult.cause());
        
    );

如果有帮助,很乐意提供更多详细信息/上下文。感谢您的考虑。

【问题讨论】:

【参考方案1】:

这与queryStreamWithParams 有关,虽然我不知道具体是什么。最后我这样解决了:

private void account(Message<JsonObject> message) 
    JsonArray params0 = new JsonArray().add(message.body().getString("username"));

    JsonArray games = new JsonArray();
    JsonArray friends = new JsonArray();
    JsonObject invitations = new JsonObject();
    JsonArray outboundArray = new JsonArray();
    JsonArray inboundArray = new JsonArray();
    JsonObject response = new JsonObject();
    response.put("response", "account");

    String query0 = "select identifier, user1 from games where accepted = 'true' and user0 = ?";
    dbClient.queryWithParams(query0, params0, res0 -> 
        if (res0.succeeded()) 
            ResultSet resultSet0 = res0.result();
            List<String> pages0a = resultSet0.getResults()
                    .stream()
                    .map(json -> json.getString(0))
                    .sorted()
                    .collect(Collectors.toList());
            List<String> pages0b = resultSet0.getResults()
                    .stream()
                    .map(json -> json.getString(1))
                    .sorted()
                    .collect(Collectors.toList());
            for (int i = 0; i < pages0a.size(); i++) 
                JsonObject game = new JsonObject();
                game.put("user0", message.body().getString("username"));
                game.put("user1", pages0b.get(i));
                game.put("identifier", pages0a.get(i));
                games.add(game);
            
            String query1 = "select identifier, user0 from games where accepted = 'true' and user1 = ?";
            dbClient.queryWithParams(query1, params0, res1 -> 
                if (res1.succeeded()) 
                    ResultSet resultSet1 = res1.result();
                    List<String> pages1a = resultSet1.getResults()
                            .stream()
                            .map(json -> json.getString(0))
                            .sorted()
                            .collect(Collectors.toList());
                    List<String> pages1b = resultSet1.getResults()
                            .stream()
                            .map(json -> json.getString(1))
                            .sorted()
                            .collect(Collectors.toList());
                    for (int i = 0; i < pages1a.size(); i++) 
                        JsonObject game = new JsonObject();
                        game.put("user0", pages1b.get(i));
                        game.put("user1", message.body().getString("username"));
                        game.put("identifier", pages1a.get(i));
                        games.add(game);
                    
                    String query2 = "select username from users";
                    dbClient.query(query2, res2 -> 
                        if (res2.succeeded()) 
                            ResultSet resultSet2 = res2.result();
                            List<String> pages2 = resultSet2.getResults()
                                    .stream()
                                    .map(json -> json.getString(0))
                                    .sorted()
                                    .collect(Collectors.toList());
                            for(String page : pages2) 
                                friends.add(page);
                            

                            String query3 = "select identifier, user1 from games where user0 = ? and accepted = ?";
                            JsonArray params3 = new JsonArray().add(message.body().getString("username")).add(false);
                            dbClient.queryWithParams(query3, params3, res3 -> 
                                if (res3.succeeded()) 
                                    ResultSet resultSet3 = res3.result();
                                    List<String> pages3a = resultSet3.getResults()
                                            .stream()
                                            .map(json -> json.getString(0))
                                            .sorted()
                                            .collect(Collectors.toList());
                                    List<String> pages3b = resultSet3.getResults()
                                            .stream()
                                            .map(json -> json.getString(1))
                                            .sorted()
                                            .collect(Collectors.toList());
                                    for (int i = 0; i < pages3a.size(); i++) 
                                        JsonObject invitation = new JsonObject();
                                        invitation.put("identifier", pages3a.get(i));
                                        invitation.put("user1", pages3b.get(i));
                                        outboundArray.add(invitation);
                                    
                                    String query4 = "select identifier, user0 from games where user1 = ? and accepted = ?";
                                    dbClient.queryWithParams(query4, params3, res4 -> 
                                        if (res4.succeeded()) 
                                            ResultSet resultSet4 = res4.result();
                                            List<String> pages4a = resultSet4.getResults()
                                                    .stream()
                                                    .map(json -> json.getString(0))
                                                    .sorted()
                                                    .collect(Collectors.toList());
                                            List<String> pages4b = resultSet4.getResults()
                                                    .stream()
                                                    .map(json -> json.getString(1))
                                                    .sorted()
                                                    .collect(Collectors.toList());
                                            for (int i = 0; i < pages4a.size(); i++) 
                                                JsonObject invitation = new JsonObject();
                                                invitation.put("identifier", pages4a.get(i));
                                                invitation.put("user0", pages4b.get(i));
                                                inboundArray.add(invitation);
                                            
                                            /* * */
                                            response.put("friends", friends);
                                            response.put("games", games);
                                            invitations.put("outbound", outboundArray);
                                            invitations.put("inbound", inboundArray);
                                            response.put("invitations", invitations);
                                            /* * */
                                            message.reply(response);
                                         else 
                                            // Failed!
                                        
                                    );
                                 else 
                                    // Failed!
                                
                            );
                         else 
                            // Failed!
                        
                    );
                 else 
                    // Failed!
                
            );
         else 
            // Failed!
        
    );


^如果您知道如何将其重构为更清洁 - 我将不胜感激。

【讨论】:

以上是关于Vert.x SQLConnection:“路由中出现意外异常”,“等待 30000(毫秒)回复后超时”的主要内容,如果未能解决你的问题,请参考以下文章

Vert.x系列(零),开篇,认识Vert.x并创建一个Http服务

Vert.x:Java 中的 Vert.x FTP 客户端是不是有工作示例?

Vert.x初体验

Vert.x 创建HTTP服务-原理篇

Vert.x 入门实战

Vert.x 操作数据库