Skip to content

Commit 22ed7cc

Browse files
committed
通过缓存 Request 校验规则来大幅提升增删改等非开放请求的性能;解决 关闭权限验证情况下批量新增、批量修改依然会验证权限 Tencent#164
1 parent 686cd45 commit 22ed7cc

File tree

4 files changed

+106
-28
lines changed

4 files changed

+106
-28
lines changed

APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,8 @@ public void onTableArrayParse(String key, JSONArray value) throws Exception {
568568
int maxUpdateCount = parser.getMaxUpdateCount();
569569

570570
String idKey = parser.createSQLConfig().getIdKey(); //Table[]: [{}] arrayConfig 为 null
571+
boolean isNeedVerifyContent = parser.isNeedVerifyContent();
572+
571573
for (int i = 0; i < valueArray.size(); i++) { //只要有一条失败,则抛出异常,全部失败
572574
//TODO 改成一条多 VALUES 的 SQL 性能更高,报错也更会更好处理,更人性化
573575
JSONObject item;
@@ -580,13 +582,13 @@ public void onTableArrayParse(String key, JSONArray value) throws Exception {
580582
JSONRequest req = new JSONRequest(childKey, item);
581583

582584
//parser.getMaxSQLCount() ? 可能恶意调用接口,把数据库拖死
583-
JSONObject result = (JSONObject) onChildParse(0, "" + i, parser.parseCorrectRequest(method, childKey, version, "", req, maxUpdateCount, parser));
585+
JSONObject result = (JSONObject) onChildParse(0, "" + i, isNeedVerifyContent == false ? req : parser.parseCorrectRequest(method, childKey, version, "", req, maxUpdateCount, parser));
584586
result = result.getJSONObject(childKey);
585587
//
586588
boolean success = JSONResponse.isSuccess(result);
587589
int count = result == null ? null : result.getIntValue(JSONResponse.KEY_COUNT);
588590

589-
if (success == false || count != 1) { //如果 code = 200 但 count != 1,不能算成功,掩盖了错误为不好排查问题
591+
if (success == false || count != 1) { //如果 code = 200 但 count != 1,不能算成功,掩盖了错误不好排查问题
590592
throw new ServerException("批量新增/修改失败!" + key + "/" + i + ":" + (success ? "成功但 count != 1 !" : (result == null ? "null" : result.getString(JSONResponse.KEY_MSG))));
591593
}
592594

APIJSONORM/src/main/java/apijson/orm/AbstractParser.java

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
import java.sql.Savepoint;
1515
import java.util.ArrayList;
1616
import java.util.Arrays;
17+
import java.util.Comparator;
1718
import java.util.HashMap;
1819
import java.util.LinkedHashSet;
1920
import java.util.List;
2021
import java.util.Map;
2122
import java.util.Map.Entry;
2223
import java.util.Set;
24+
import java.util.SortedMap;
25+
import java.util.TreeMap;
2326
import java.util.concurrent.TimeoutException;
2427

2528
import javax.activation.UnsupportedDataTypeException;
@@ -472,7 +475,7 @@ public JSONObject parseCorrectRequest(RequestMethod method, String tag, int vers
472475
JSONObject object = null;
473476
String error = "";
474477
try {
475-
object = getStructure("Request", JSONRequest.KEY_TAG, tag, version);
478+
object = getStructure("Request", method.name(), tag, version);
476479
} catch (Exception e) {
477480
error = e.getMessage();
478481
}
@@ -665,34 +668,91 @@ public JSONObject parseCorrectResponse(String table, JSONObject response) throws
665668

666669
/**获取Request或Response内指定JSON结构
667670
* @param table
668-
* @param key
669-
* @param value
671+
* @param method
672+
* @param tag
670673
* @param version
671674
* @return
672675
* @throws Exception
673676
*/
674677
@Override
675-
public JSONObject getStructure(@NotNull String table, String key, String value, int version) throws Exception {
676-
//获取指定的JSON结构 <<<<<<<<<<<<<<
677-
SQLConfig config = createSQLConfig().setMethod(GET).setTable(table);
678-
config.setPrepared(false);
679-
config.setColumn(Arrays.asList("structure"));
680-
681-
Map<String, Object> where = new HashMap<String, Object>();
682-
where.put("method", requestMethod.name());
683-
if (key != null) {
684-
where.put(key, value);
685-
}
686-
if (version > 0) {
687-
where.put(JSONRequest.KEY_VERSION + "{}", ">=" + version);
688-
}
689-
config.setWhere(where);
690-
config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-"));
691-
config.setCount(1);
692-
693-
//too many connections error: 不try-catch,可以让客户端看到是服务器内部异常
694-
JSONObject result = getSQLExecutor().execute(config, false);
695-
return getJSONObject(result, "structure");//解决返回值套了一层 "structure":{}
678+
public JSONObject getStructure(@NotNull String table, String method, String tag, int version) throws Exception {
679+
// TODO 目前只使用 Request 而不使用 Response,所以这里写死用 REQUEST_MAP,以后可能 Response 表也会与 Request 表合并,用字段来区分
680+
String cacheKey = AbstractVerifier.getCacheKeyForRequest(method, tag);
681+
SortedMap<Integer, JSONObject> versionedMap = AbstractVerifier.REQUEST_MAP.get(cacheKey);
682+
683+
JSONObject result = versionedMap == null ? null : versionedMap.get(Integer.valueOf(version));
684+
if (result == null) { // version <= 0 时使用最新,version > 0 时使用 > version 的最接近版本(最小版本)
685+
Set<Entry<Integer, JSONObject>> set = versionedMap == null ? null : versionedMap.entrySet();
686+
687+
if (set != null && set.isEmpty() == false) {
688+
Entry<Integer, JSONObject> maxEntry = null;
689+
690+
for (Entry<Integer, JSONObject> entry : set) {
691+
if (entry == null || entry.getKey() == null || entry.getValue() == null) {
692+
continue;
693+
}
694+
695+
if (version <= 0 || version == entry.getKey()) { // 这里应该不会出现相等,因为上面 versionedMap.get(Integer.valueOf(version))
696+
maxEntry = entry;
697+
break;
698+
}
699+
700+
if (entry.getKey() < version) {
701+
break;
702+
}
703+
704+
maxEntry = entry;
705+
}
706+
707+
result = maxEntry == null ? null : maxEntry.getValue();
708+
}
709+
710+
if (result != null) { // 加快下次查询,查到值的话组合情况其实是有限的,不属于恶意请求
711+
if (versionedMap == null) {
712+
versionedMap = new TreeMap<>(new Comparator<Integer>() {
713+
714+
@Override
715+
public int compare(Integer o1, Integer o2) {
716+
return o2 == null ? -1 : o2.compareTo(o1); // 降序
717+
}
718+
});
719+
}
720+
721+
versionedMap.put(Integer.valueOf(version), result);
722+
AbstractVerifier.REQUEST_MAP.put(cacheKey, versionedMap);
723+
}
724+
}
725+
726+
if (result == null) {
727+
if (AbstractVerifier.REQUEST_MAP.isEmpty() == false) {
728+
return null; // 已使用 REQUEST_MAP 缓存全部,但没查到
729+
}
730+
731+
//获取指定的JSON结构 <<<<<<<<<<<<<<
732+
SQLConfig config = createSQLConfig().setMethod(GET).setTable(table);
733+
config.setPrepared(false);
734+
config.setColumn(Arrays.asList("structure"));
735+
736+
Map<String, Object> where = new HashMap<String, Object>();
737+
where.put("method", method);
738+
where.put(JSONRequest.KEY_TAG, tag);
739+
740+
if (version > 0) {
741+
where.put(JSONRequest.KEY_VERSION + "{}", ">=" + version);
742+
}
743+
config.setWhere(where);
744+
config.setOrder(JSONRequest.KEY_VERSION + (version > 0 ? "+" : "-"));
745+
config.setCount(1);
746+
747+
//too many connections error: 不try-catch,可以让客户端看到是服务器内部异常
748+
result = getSQLExecutor().execute(config, false);
749+
750+
// version, method, tag 组合情况太多了,JDK 里又没有 LRUCache,所以要么启动时一次性缓存全部后面只用缓存,要么每次都查数据库
751+
// versionedMap.put(Integer.valueOf(version), result);
752+
// AbstractVerifier.REQUEST_MAP.put(cacheKey, versionedMap);
753+
}
754+
755+
return getJSONObject(result, "structure"); //解决返回值套了一层 "structure":{}
696756
}
697757

698758

APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@
3434
import java.util.Collection;
3535
import java.util.HashMap;
3636
import java.util.HashSet;
37+
import java.util.LinkedHashMap;
3738
import java.util.LinkedHashSet;
3839
import java.util.List;
3940
import java.util.Map;
4041
import java.util.Map.Entry;
4142
import java.util.Set;
43+
import java.util.SortedMap;
4244
import java.util.regex.Pattern;
4345

4446
import javax.activation.UnsupportedDataTypeException;
@@ -83,9 +85,17 @@ public abstract class AbstractVerifier<T> implements Verifier<T>, IdCallback {
8385

8486
// <TableName, <METHOD, allowRoles>>
8587
// <User, <GET, [OWNER, ADMIN]>>
88+
@NotNull
8689
public static final Map<String, Map<RequestMethod, RequestRole[]>> SYSTEM_ACCESS_MAP;
90+
@NotNull
8791
public static final Map<String, Map<RequestMethod, RequestRole[]>> ACCESS_MAP;
92+
93+
// <method tag, <version, Request>>
94+
// <PUT Comment, <1, { "method":"PUT", "tag":"Comment", "structure":{ "MUST":"id"... }... }>>
95+
@NotNull
96+
public static final Map<String, SortedMap<Integer, JSONObject>> REQUEST_MAP;
8897

98+
@NotNull
8999
public static final Map<String, Pattern> COMPILE_MAP;
90100
static {
91101
SYSTEM_ACCESS_MAP = new HashMap<String, Map<RequestMethod, RequestRole[]>>();
@@ -110,6 +120,8 @@ public abstract class AbstractVerifier<T> implements Verifier<T>, IdCallback {
110120
}
111121

112122
ACCESS_MAP = new HashMap<>(SYSTEM_ACCESS_MAP);
123+
124+
REQUEST_MAP = new HashMap<>(ACCESS_MAP.size()*6); // 单个与批量增删改
113125

114126
COMPILE_MAP = new HashMap<String, Pattern>();
115127
}
@@ -689,7 +701,7 @@ public static JSONObject verifyResponse(@NotNull final RequestMethod method, fin
689701
+ "\n response = \n" + JSON.toJSONString(response));
690702

691703
if (target == null || response == null) {// || target.isEmpty() {
692-
Log.i(TAG, "verifyRequest target == null || response == null >> return response;");
704+
Log.i(TAG, "verifyResponse target == null || response == null >> return response;");
693705
return response;
694706
}
695707

@@ -1424,5 +1436,9 @@ public static void verifyRepeat(String table, String key, Object value, long exc
14241436
}
14251437
}
14261438

1439+
public static String getCacheKeyForRequest(String method, String tag) {
1440+
return method + " " + tag;
1441+
}
1442+
14271443

14281444
}

APIJSONORM/src/main/java/apijson/orm/Parser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ JSONObject parseCorrectRequest(RequestMethod method, String tag, int version, St
7474

7575
JSONObject parseCorrectResponse(String table, JSONObject response) throws Exception;
7676

77-
JSONObject getStructure(String table, String key, String value, int version) throws Exception;
77+
JSONObject getStructure(String table, String method, String tag, int version) throws Exception;
7878

7979

8080

0 commit comments

Comments
 (0)