Введение
Содержание этой статьи состоит в том, чтобы реализовать идемпотентную проверку интерфейса.Узнайте больше руководств по серии springboot для тех, кто ищет знания, и посмотрите официальный альбом учетной записи;Идемпотентность интерфейса, вообще говоря, и только один запрос завершается успешно, когда несколько запросов инициируются в в то же время., Его цель состоит в том, чтобы предотвратить множественные представления, повторное хранение данных, а также формировать задержки проверки сети и повторные представления; публика:искатель знаний
Искатель знаний (Наследуя дух открытого исходного кода, Распространение знаний о технологиях;)
Два плана реализации
Основные реализации следующие
2.1 Уникальный индекс
Добавление уникального индекса в таблицу является самым простым методом.При повторной вставке данных об исключении SQL будет сообщено напрямую, что мало повлияет на приложение;
alter table 表名 add unique(字段)
Например, два поля являются уникальными индексами.Если встречается точно такое же имя_заказа, create_time будет сообщать об исключении напрямую и повторно;
alter table `order` add unique(order_name,create_time)
2.2 Блокировка
Распределенные блокировки также могут реализовывать идемпотентную проверку интерфейсов.Искатели знаний написали документ об идее использования Redis для реализации распределенных блокировок.Друзья могут обратиться к следующему«Почему бы вам не перераспределить распределенную блокировку? потому что вы не видели эту статью"
Используйте оптимистическую блокировку (реализованную на основе номера версии) или пессимистическую блокировку (блокировку таблицы или строки);
2.3 Сначала спроси, а потом суди
При входе на склад сначала запросите есть ли данные, нет вставки, иначе не вставится;
2.4 жетонный механизм
Механизм токена также находится в центре внимания этой статьи; общая идея состоит в том, чтобы сначала перейти к Redis, чтобы получить токен при инициировании запроса, поместить полученный токен в прослушиватель запроса, перехватить запрос, когда запрос достигает сервера, и проверить токен в прослушивателе запроса. , чтобы проверить, если проверка прошла, отменить перехват и удалить токен, в противном случае используйте пользовательское исключение для возврата сообщения об ошибке;
Три использования Redis для проверки идемпотентности интерфейса
3.1 класс инструментов Redis
Чтобы настроить RedisTemplate, обратитесь к статье, опубликованной Knowledge Seeker.«интегрированный redis Springboot (базовый)»
/**
* @Author lsc
* <p> </p>
*/
@Component
public class RedisUtils {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 判断key是否存在
* @param key 键
* @return boolean
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除key
* @param key 键
*/
public Boolean del(String key) {
if (key != null && key.length() > 0) {
return redisTemplate.delete(key);
}else {
return false;
}
}
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
3.2 Класс инструмента токена
Используйте uuid для генерации случайной строки, предотвратите расшифровку токена через шифрование md5 и обеспечьте уникальность и безопасность токена; установите время истечения 30 секунд, то есть только один успешный запрос может быть отправлен в течение 30 секунд. В соответствии с различными потребностями бизнеса читатели справляются с этим сами;
/**
* @Author lsc
* <p> </p>
*/
@Component
public class TokenUtis {
@Autowired
RedisUtils redisUtils;
// token 过期时间为30秒
private final static Long TOKEN_EXPIRE = 30L;
private final static String TOKEN_NAME = "token";
/* *
* @Author lsc
* <p> 生成token 放入缓存</p>
* @Param []
*/
public String generateToken() {
String uuid = UUID.randomUUID().toString();
String token = DigestUtils.md5DigestAsHex(uuid.getBytes());
redisUtils.set(TOKEN_NAME,token,TOKEN_EXPIRE);
return token;
}
/* *
* @Author lsc
* <p> token 校验 </p>
* @Param [request]
*/
public boolean verifyToken(HttpServletRequest request) {
String token = request.getHeader(TOKEN_NAME);
// header中不存在token
if(StringUtils.isEmpty(token)) {
// 抛出自定义异常
System.out.println("token不存在");
throw new GlobleException(CodeMsg.BAD_REQUEST);
}
// 缓存中不出在
if(!redisUtils.hasKey(TOKEN_NAME)) {
// 抛出自定义异常
System.out.println("token已经过期");
throw new GlobleException(CodeMsg.BAD_REQUEST);
}
String cachToekn = (String)redisUtils.get(TOKEN_NAME);
if (!token.equals(cachToekn)){
// 抛出自定义异常
System.out.println("token校验失败");
throw new GlobleException(CodeMsg.BAD_REQUEST);
}
// 移除token
Boolean del = redisUtils.del(TOKEN_NAME);
if (!del){
// 抛出自定义异常
System.out.println("token删除失败");
throw new GlobleException(CodeMsg.BAD_REQUEST);
}
return true;
}
}
3.3 Аннотации
Определите аннотации, которые используются в методах.Когда метод уровня управления аннотирован, это указывает, что запрос является идемпотентным запросом;
/**
* @Author lsc
* <p> </p>
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
}
3.4 Конфигурация перехватчика
Выберите пре-перехватчик, и каждый запрос будет проверять, есть ли идемпотентная аннотация в прибывающем методе, и если да, то выполнять проверку токена;
/**
* @Author lsc
* <p> </p>
*/
@Component
public class IdempotentInterceptor implements HandlerInterceptor {
@Autowired
private TokenUtis tokenUtis;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (!(handler instanceof HandlerMethod)) {
return true;
}
// 对有Idempotent注解的方法进行拦截校验
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
Idempotent methodAnnotation = method.getAnnotation(Idempotent.class);
if (methodAnnotation != null) {
// token 校验
tokenUtis.verifyToken(request);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Сопоставьте шаблон URL-адреса с перехватчиком и введите его в контейнер Spring.
/**
* @Author lsc
* <p> </p>
*/
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Autowired
IdempotentInterceptor idempotentInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求
registry.addInterceptor(idempotentInterceptor).addPathPatterns("/**");
}
}
3.5 Уровень управления
Уровень управления написан следующим образом: при инициации запроса токен получается через метод getToken, полученный токен помещается в прослушиватель, а затем запрашивается метод testIdempotent.
/**
* @Author lsc
* <p> </p>
*/
@RestController
public class ZszxzController {
@Autowired
TokenUtis tokenUtis;
@GetMapping("zszxz/token")
public ResultPage getToken(){
String token = tokenUtis.generateToken();
JSONObject jsonObject = new JSONObject();
jsonObject.put("token",token);
return ResultPage.sucess(CodeMsg.SUCESS,jsonObject);
}
@Idempotent
@GetMapping("zszxz/test")
public ResultPage testIdempotent(){
return ResultPage.sucess(CodeMsg.SUCESS,"校验成功");
}
}
3.6 Тестирование
Запрос на получение токена
Токен, который был использован, запрос сообщает об ошибке
Запрос на получение токена выполнен успешно
Для высококонкурентных запросов можно использовать jmeter для тестирования, и эта статья также может быть реализована с использованием перехвата aop;
Этот набор руководств
- Начало работы с SpringBoot (1)
- Пользовательский баннер Springboot(2)
- Анализ файла конфигурации Springboot (3)
- встроенный mybatis с пружинной загрузкой(4)
- Springboot интегрированный jdbcTemplate(5)
- Модульные тесты spingboot (6)
- springboot интегрированный тимелеаф(7)
- загрузка нескольких файлов springboot (8)
- загрузка файла springboot (9)
- Пользовательский класс исключений Springboot (10)
- многосредовая конфигурация springboot (11)
- Анализ принципа автоматической настройки springboot (12)
- springboot интегрирован вызов интерфейса restTemplate (13)
- интегрированное планирование задач springboot (14)
- Springboot кросс-доменная обработка CORS (15)
- springboot открывает сжатие GIZP (16)
- интегрированный журнал Springboot(17)
- Springboot интегрированный Swagger(18)
- Springboot встроенный фоновый мониторинг привода (19)
- Springboot объединяет mybatis+oracle+druid(20)
- springboot интегрированная пружинная сессия (21)
- интегрированный jwt с пружинной загрузкой (22)
- springboot интегрированный фоновый мониторинг администратора (23)
- Основы интегрированного Redis в Springboot (24)
- Встроенный Redis-кеш Springboot (25)
- Springboot использует перехват журнала AOP (26)
- Интегрированная в Springboot проверка параметров проверки (27)
- springboot интегрирует mybatisPlus (28)
- пружинный интегрированный широ(29)
- springboot реализует проверку идемпотентности интерфейса (30)
- веб-сокеты, интегрированные в springboot (31)
- Анализ исходного кода restTemplate (32)
- SpringBoot использует асинхронные вызовы @Async и пулы потоков (33)
- продолжение следует