起因
ショットはネットワークカードの、データポイントN回の追加につながるので、リストの結果はN.に追加されたことを言うように見えたテストを送信
分析
コードの表示には、データを挿入する前に、データに基づいて、フィールドの内容が既に存在するかどうか問い合わせがあり、更新、または追加するために存在します。依然としてこの問題が発生し、多くの場合はに導くことができます:
1、同時リクエストが入って来たときに、同時クエリがダーティデータが得られ、最終的な同時挿入、存在しない;
2、分散アプリケーションを、関連する挿入操作の実装ではありませんデータベースは、ロックテーブルを操作します。
第二の点は、より複雑な、戻し工程で、第一工程点1、同じユーザの操作(同じリクエスト)は演算結果がこのように複雑な操作要求を回避すること、第2の要求を許可する前に待たなければならない返します。
需要
図1に示すように、フロントエンドまで要求を送信した後、応答が返されない、操作無効オフボタン
2、ユーザログの記録動作の後端が、追加、変更、および削除操作はアンチリプレイを提出しました
デザイン
Redisのによって識別として操作ログおよび抗再サブミット一般に処理のためにAOP部使用し、ロックを記録することによって、インターネット上の情報を見つけます
プロセス
コメントを定義します。1.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAndNoRepeatSubmit {
String value() default "";
int lockTime() default 10;
}
2、セクション増やす方法
@Aspect
@Component
@Slf4j
public class LogAspect {
@Resource
private AsyncTaskService asyncService;
@Pointcut("@annotation(logAndNoRepeatSubmit)")
public void pointcut(LogAndNoRepeatSubmit logAndNoRepeatSubmit) {
}
@Around(value = "pointcut(logAndNoRepeatSubmit)")
public Object around(ProceedingJoinPoint point, LogAndNoRepeatSubmit logAndNoRepeatSubmit) throws Throwable {
long beginTime = System.currentTimeMillis();
int lockSeconds = logAndNoRepeatSubmit.lockTime();
String key = LogAspectUtil.getKeyFromRequest();
String clientId = UUID.randomUUID().toString();
boolean isSuccess=false;
//在并发情况下,redis请求会有大概10ms的请求时长,因此加锁,避免redis还没有执行加锁,其他请求已经进来而导致与预期不一致的情况
synchronized (this) {
isSuccess = RedisUtil.tryLock(CacheConst.IDEMPOTENCE + key, clientId, lockSeconds);
}
if (isSuccess) {
Object result = null;
try {
result = point.proceed();
} catch (Throwable e) {
log.error("不重复提交日志", e);
throw e;
} finally {
RedisUtil.del(CacheConst.IDEMPOTENCE + key);
}
long time = System.currentTimeMillis() - beginTime;
Long uid = UserCacheUtil.getUserInfo().getId();
String userName = UserCacheUtil.getUserInfo().getName();
String ip = WebUtil.getIP();
//记录操作日志
OperationLogDTO dto = LogAspectUtil.handleLog(point, time, result, uid, userName, "COM-PC", ip);
saveLog(dto);
return result;
} else {
return R.fail("重复请求,请稍后再试");
}
}
private void saveLog(OperationLogDTO dto) {
// 保存系统日志,异步方法保存,或者放到MQ去执行
asyncService.saveLog(dto);
}
}
3は、ログの内容を操作するために設計されました:
CREATE TABLE `operation_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`uid` bigint(20) DEFAULT NULL,
`user_name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
`operation` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
`response_time` bigint(5) DEFAULT NULL,
`method` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
`params` text CHARACTER SET utf8mb4,
`ip` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
`ctime` datetime DEFAULT NULL,
`status` bit(1) DEFAULT NULL,
`response_code` int(4) DEFAULT NULL,
`response_msg` varchar(1000) CHARACTER SET utf8mb4 DEFAULT NULL,
`request_side` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2690 DEFAULT CHARSET=utf-8;
4、テスト
の同時5つのテスト要求を行うJMeterの使用は、
最初の要求が完了していないとき、他のすべての要求が拒否されていることがわかります
概要
アンチ重いある程度のダーティデータの出現、分散アプリケーションの並行処理を改善するために、後続の必要性を回避提出します。