从 App Engine 构建缓存时如何平衡负载?
How to balance the load when building cache from App Engine?
我目前有以下情况,困扰我几个月了。
案例
我已经构建了一个 Java (FX) 应用程序作为我商店的现金登记处。该应用程序包含许多 class 项(例如 Customer、Customer、Transaction 等),它们与服务器 API 共享。服务器 API 托管在 Google App Engine 上。
因为我们也有一个网店,所以我选择在应用程序启动时构建整个数据库的缓存。为此,我为每个 class/table:
调用我的数据 API 的 GET
protected QueryBuilder performGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException, ApiException, JSONException {
Connection conn = connectToCloudSQL();
log.info("Parameters: "+Functions.parameterMapToString(req.getParameterMap()));
String tableName = this.getTableName(req);
log.info("TableName: "+tableName);
GetQueryBuilder queryBuilder = DataManager.executeGet(conn, req.getParameterMap(), tableName, null);
//Get the correct method to create the objects
String camelTableName = Functions.snakeToCamelCase(tableName);
String parsedTableName = Character.toUpperCase(camelTableName.charAt(0)) + camelTableName.substring(1);
List<Object> objects = new ArrayList<>();
try {
log.info("Parsed Table Name: "+parsedTableName);
Method creationMethod = ObjectManager.class.getDeclaredMethod("create"+parsedTableName, ResultSet.class, boolean.class);
while (queryBuilder.getResultSet().next()) {
//Create new objects with the ObjectManager
objects.add(creationMethod.invoke(null, queryBuilder.getResultSet(), false));
}
log.info("List of objects created");
creationMethod = null;
}
catch (Exception e) {
camelTableName = null;
parsedTableName = null;
objects = null;
throw new ApiException(e, "Something went wrong while iterating through ResultSet.", ErrorStatus.NOT_VALID);
}
Functions.listOfObjectsToJson(objects, res.getOutputStream());
log.info("GET Request succeeded");
//Clean up objects
camelTableName = null;
parsedTableName = null;
objects = null;
closeConnection(conn);
return queryBuilder;
}
它简单地从我的云 SQL 数据库中请求的 table 获取每一行。然后它使用与客户端应用程序共享的 class 创建对象。最后,它使用 GSON 将这些 classes 转换为 JSON。我的一些 table 有 10.000 多行,然后大约需要。 5-10 秒完成此操作。
在客户端,我使用相同的共享 class 将此 JSON 转换回对象列表。首先,我按顺序加载基本的 classes(因为否则应用程序将无法启动),然后我在后台使用单独的线程加载其余 classes。
问题
每次我加载缓存时,服务器都有可能(1 对 4)在一些较大的 table 上响应 DeadlineExceededException
。我认为这与 Google App Engine 无法及时启动新实例有关,因此计算时间超过了限制。
我知道这与在后台线程中加载对象有关,因为这些都是同时启动的。当我将这些线程的启动延迟 3 秒时,错误发生的次数少了很多,但仍然存在。因为应用程序在后台加载 15 classes,延迟它们并不理想,因为应用程序在完成之前只能部分工作。在开始之前加载所有内容也不是一个选项,因为这将花费超过 2 分钟的时间。
有人知道如何为此在 Google App Engine 上设置一些负载平衡吗?我想解决这个服务器端问题。
您显然遇到了预热请求和需要很长时间的查询的问题。您有通常的选择:
- 进行一些分析并降低方法调用的成本
- 使用缓存 (memcache) 缓存部分结果
如果这些选项对您不起作用,您应该并行化计算。我想到的一件事是,如果您像这样简单地将请求分成多个并行请求,就可以可靠地减少请求时间:
- 假设您的 table 包含 5k 行。
- 然后您创建 50 个请求,每个请求处理 100 行。
- 在服务器或客户端聚合结果并响应
仅在服务器端执行此操作将非常困难,但如果您现在(小得多)的任务 return 在几秒钟内完成,这应该是可能的。
或者,您可以一次 return 一个作业 ID,然后让客户端在几秒钟内轮询结果。然而,这将需要在客户端进行小的更改。恕我直言,这是更好的选择,尤其是如果您想使用任务队列来创建响应。
我目前有以下情况,困扰我几个月了。
案例
我已经构建了一个 Java (FX) 应用程序作为我商店的现金登记处。该应用程序包含许多 class 项(例如 Customer、Customer、Transaction 等),它们与服务器 API 共享。服务器 API 托管在 Google App Engine 上。
因为我们也有一个网店,所以我选择在应用程序启动时构建整个数据库的缓存。为此,我为每个 class/table:
调用我的数据 API 的 GETprotected QueryBuilder performGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException, ApiException, JSONException {
Connection conn = connectToCloudSQL();
log.info("Parameters: "+Functions.parameterMapToString(req.getParameterMap()));
String tableName = this.getTableName(req);
log.info("TableName: "+tableName);
GetQueryBuilder queryBuilder = DataManager.executeGet(conn, req.getParameterMap(), tableName, null);
//Get the correct method to create the objects
String camelTableName = Functions.snakeToCamelCase(tableName);
String parsedTableName = Character.toUpperCase(camelTableName.charAt(0)) + camelTableName.substring(1);
List<Object> objects = new ArrayList<>();
try {
log.info("Parsed Table Name: "+parsedTableName);
Method creationMethod = ObjectManager.class.getDeclaredMethod("create"+parsedTableName, ResultSet.class, boolean.class);
while (queryBuilder.getResultSet().next()) {
//Create new objects with the ObjectManager
objects.add(creationMethod.invoke(null, queryBuilder.getResultSet(), false));
}
log.info("List of objects created");
creationMethod = null;
}
catch (Exception e) {
camelTableName = null;
parsedTableName = null;
objects = null;
throw new ApiException(e, "Something went wrong while iterating through ResultSet.", ErrorStatus.NOT_VALID);
}
Functions.listOfObjectsToJson(objects, res.getOutputStream());
log.info("GET Request succeeded");
//Clean up objects
camelTableName = null;
parsedTableName = null;
objects = null;
closeConnection(conn);
return queryBuilder;
}
它简单地从我的云 SQL 数据库中请求的 table 获取每一行。然后它使用与客户端应用程序共享的 class 创建对象。最后,它使用 GSON 将这些 classes 转换为 JSON。我的一些 table 有 10.000 多行,然后大约需要。 5-10 秒完成此操作。
在客户端,我使用相同的共享 class 将此 JSON 转换回对象列表。首先,我按顺序加载基本的 classes(因为否则应用程序将无法启动),然后我在后台使用单独的线程加载其余 classes。
问题
每次我加载缓存时,服务器都有可能(1 对 4)在一些较大的 table 上响应 DeadlineExceededException
。我认为这与 Google App Engine 无法及时启动新实例有关,因此计算时间超过了限制。
我知道这与在后台线程中加载对象有关,因为这些都是同时启动的。当我将这些线程的启动延迟 3 秒时,错误发生的次数少了很多,但仍然存在。因为应用程序在后台加载 15 classes,延迟它们并不理想,因为应用程序在完成之前只能部分工作。在开始之前加载所有内容也不是一个选项,因为这将花费超过 2 分钟的时间。
有人知道如何为此在 Google App Engine 上设置一些负载平衡吗?我想解决这个服务器端问题。
您显然遇到了预热请求和需要很长时间的查询的问题。您有通常的选择:
- 进行一些分析并降低方法调用的成本
- 使用缓存 (memcache) 缓存部分结果
如果这些选项对您不起作用,您应该并行化计算。我想到的一件事是,如果您像这样简单地将请求分成多个并行请求,就可以可靠地减少请求时间:
- 假设您的 table 包含 5k 行。
- 然后您创建 50 个请求,每个请求处理 100 行。
- 在服务器或客户端聚合结果并响应
仅在服务器端执行此操作将非常困难,但如果您现在(小得多)的任务 return 在几秒钟内完成,这应该是可能的。
或者,您可以一次 return 一个作业 ID,然后让客户端在几秒钟内轮询结果。然而,这将需要在客户端进行小的更改。恕我直言,这是更好的选择,尤其是如果您想使用任务队列来创建响应。