【问题标题】:Are there major scaling limits with play framework and jdbc blocking io calls播放框架和 jdbc 阻塞 io 调用是否存在重大扩展限制
【发布时间】:2015-09-23 21:22:07
【问题描述】:

我正在使用 Java 的 playframework (2.4) 并将其连接到 Postgres。 play 框架被用作一个 RESTful 服务,它所做的只是使用 JDBC 进行插入、更新、读取和删除。在这个播放页面https://www.playframework.com/documentation/2.3.x/JavaAsync 上,它清楚地指出 JDBC 是阻塞的,并且播放的线程很少。对于知道这一点的人来说,这有多大的限制,有什么办法可以解决这个问题吗?我的特定应用每秒可以有几百个数据库调用。我将拥有所有的硬件和额外的服务器,但不知道 play 如何处理这个或扩展以在代码中处理这个。我的代码如下所示:

public static Result myprofile() {
        DynamicForm requestData = Form.form().bindFromRequest();
        Integer id = Integer.parseInt(requestData.get("id"));

        try {
            JSONObject jo = null;
            Connection conn = DB.getConnection();
            ResultSet rs;
            JSONArray ja = new JSONArray();
            PreparedStatement ps = conn.prepareStatement("SELECT p.fullname as fullname, s.post as post,to_char(s.created_on, 'MON DD,YYYY') as created_on,s.last_reply as last_reply,s.id as id,s.comments as comments,s.state as state,s.city as city,s.id as id FROM profiles as p INNER JOIN streams as s ON (s.profile_id=p.id) WHERE s.profile_id=? order by created_on desc");
            ps.setInt(1, id);
            rs = ps.executeQuery();

            while (rs.next()) {
                jo = new JSONObject();
                jo.put("fullname", rs.getString("fullname"));
                jo.put("post", rs.getString("post"));
                jo.put("city", rs.getString("city"));
                jo.put("state", rs.getString("state"));
                jo.put("comments", rs.getInt("comments"));
                jo.put("id", rs.getInt("id"));
                jo.put("last_reply", difference(rs.getInt("last_reply"), rs.getString("created_on")));
                ja.put(jo);
            }
            JSONObject mainObj = new JSONObject();
            mainObj.put("myprofile", ja);


            String total = mainObj.toString();
            System.err.println(total);
            conn.close();
            return ok(total);
        } catch (Exception e) {
            e.getMessage();
        }
        return ok();
    }

我也知道我可以尝试将其包装在期货承诺中,但仍然会发生阻塞。如前所述,我将处理所有服务器和其他东西,但是播放框架是否能够使用 jdbc 扩展到每秒数百个请求?我现在正在询问和学习,以避免以后出现严重错误。

【问题讨论】:

  • 拥有多个阻塞线程应该没问题,每个线程都用于一个 JDBC 连接。
  • 哦,好吧,我的印象是 play 有 4 个线程,并且每秒每个请求都绑定一个。
  • 您需要在不同的线程轮询上执行阻塞 IO 操作。

标签: java jdbc playframework playframework-2.3


【解决方案1】:

Play 完全可以处理这种负载。

文档指出,在控制器方法中应避免阻塞代码 - 默认配置已针对它们进行了调整以实现异步执行。如果您在其中插入一些阻塞调用,您的控制器现在将等待该调用完成,然后才能处理另一个传入请求 - 这很糟糕。

你不能通过包装神奇地将同步 IO 变成异步 它在一个承诺中。如果您无法将应用程序的架构更改为 避免阻塞操作,在某些时候该操作将不得不 被执行,并且该线程将被阻塞。所以除了 将操作包含在 Promise 中,需要配置它 在已配置的单独执行上下文中运行 足够的线程来处理预期的并发。看 了解 Play 线程池以获取更多信息。 https://www.playframework.com/documentation/2.4.x/JavaAsync#Make-controllers-asynchronous

我相信您已经意识到这一点,但我想指出粗体部分。您的数据库中可供应用程序调用的线程数量有限 - 跟踪此数量可能会有所帮助,为这些线程创建一个新的执行上下文,并将该新执行上下文分配给一个承诺包装您的数据库调用。

查看这篇关于 Play 应用程序转向的帖子,它应该让您了解这是什么样的。我相信他正在使用 Akka Actors,这可能超出了您的范围,但线程调整的想法是相同的:

Play 2 针对 HTTP 请求进行了开箱即用的优化, 包含阻塞调用(即异步)。大多数数据库驱动的应用程序 在 Java 中通过 JDBC 使用同步调用,所以 Play 2 需要一些额外的 为这些类型的请求调整 Akka 的配置。 http://www.jamesward.com/2012/06/25/optimizing-play-2-for-database-driven-apps

如果您尝试在不打开线程的情况下在数据库上执行大量请求,您将面临使应用程序的其余线程饿死的风险,这将停止您的应用程序。对于您预期的负载,默认调整可能没问题,但值得进行一些额外的调查。

开始使用线程调优: https://www.playframework.com/documentation/2.4.x/ThreadPools

您应该更新您的控制器以返回 Promise,并且在 Play 2.4 中也没有理由再将其设为静态。 https://www.playframework.com/documentation/2.4.x/Migration24#Routing


在 application.conf 中定义一个名为“jdbc-execution-context”的执行上下文

//reference to context
ExecutionContext jdbcExecutionContext = Akka.system().dispatchers()
     .lookup("jdbc-execution-context");

return promise(() -> {
    //db call
}, jdbcExecutionContext)
.map(callResult -> ok(callResult));

【讨论】:

  • 非常感谢您的解释,这真的很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-27
  • 1970-01-01
  • 2012-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多