Spring boot - + - redis - implements interface idempotency. It's great to read this article!

introduce

The concept of idempotency is that the impact of any multiple executions is the same as that of one execution. According to this meaning, the final explanation is that the impact on the database can only be one-time and cannot be processed repeatedly. The means are as follows

  1. Create a unique index of the database

  2. token mechanism

  3. Pessimistic lock or optimistic lock

  4. Query before judgment

I will mainly introduce you to Redis's implementation of automatic idempotency. Its principle is shown in the figure below.

Implementation process

Introducing maven dependency

 <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-redis</artifactId>

        </dependency>

spring configuration file write

server.port=8080

core.datasource.druid.enabled=true

core.datasource.druid.url=jdbc:mysql://192.168.1.225:3306/?useUnicode=true&characterEncoding=UTF-8

core.datasource.druid.username=root

core.datasource.druid.password=

core.redis.enabled=true

spring.redis.host=192.168.1.225 #redis address of this machine

spring.redis.port=16379

spring.redis.database=3

spring.redis.jedis.pool.max-active=10

spring.redis.jedis.pool.max-idle=10

spring.redis.jedis.pool.max-wait=5s

spring.redis.jedis.pool.min-idle=10

Introducing Redis

Introduce the redis related stators in spring boot, and then use the RedisTemplate encapsulated in spring boot

package cn.smallmartial.demo.utils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.core.ValueOperations;

import org.springframework.stereotype.Component;

import java.io.Serializable;

import java.util.Objects;

import java.util.concurrent.TimeUnit;

/**

 * @Author smallmartial

 * @Date 2020/4/16

 * @Email smallmarital@qq.com

 */

@Component

public class RedisUtil {

    @Autowired

    private RedisTemplate redisTemplate;

 /**

     * Write cache

     *

     * @param key

     * @param value

     * @return

     */

    public boolean set(final String key, Object value) {

        boolean result = false;

        try {

            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();

            operations.set(key, value);

            result = true;

        } catch (Exception e) {

            e.printStackTrace();

        }

 return result;

    }

 /**

     * Write cache setting time

     *

     * @param key

     * @param value

     * @param expireTime

     * @return

     */

    public boolean setEx(final String key, Object value, long expireTime) {

        boolean result = false;

        try {

            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();

            operations.set(key, value);

            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);

            result = true;

        } catch (Exception e) {

            e.printStackTrace();

        }

 return result;

    }

 /**

     * Read cache

     *

     * @param key

     * @return

     */

    public Object get(final String key) {

        Object result = null;

        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();

        result = operations.get(key);

 return result;

    }

 /**

     * Delete the corresponding value

     *

     * @param key

     */

    public boolean remove(final String key) {

 if (exists(key)) {

            Boolean delete = redisTemplate.delete(key);

 return delete;

        }

 return false;

    }

 /**

     * Determine whether the key exists

     *

     * @param key

     * @return

     */

    public boolean exists(final String key) {

        boolean result = false;

        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();

 if (Objects.nonNull(operations.get(key))) {

            result = true;

        }

 return result;

    }

}

Custom annotation

Customize an annotation. The purpose of defining this annotation is to add it to the method that needs to implement idempotent. As long as a method annotates its, it will automatically implement idempotent operation. Its code is as follows

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface AutoIdempotent {

}

Creation and implementation of token

Token service interface. We create a new interface to create a token service. There are mainly two methods: one is used to create a token and the other is used to verify a token

public interface TokenService {

 /**

     * Create token

     * @return

     */

    public  String createToken();

 /**

     * Check token

     * @param request

     * @return

     */

    public boolean checkToken(HttpServletRequest request) throws Exception;

}

The implementation class of the token refers to the implementation class of the service. The token refers to the redis service. The token is created by using the random algorithm tool class to generate a random uuid string, which is then put into redis. If the putting is successful, the token is returned. The verification method is to obtain the value of the token from the header. If it does not exist, the exception will be directly run, This exception information can be directly intercepted and returned to the front end.

package cn.smallmartial.demo.service.impl;

import cn.smallmartial.demo.bean.RedisKeyPrefix;

import cn.smallmartial.demo.bean.ResponseCode;

import cn.smallmartial.demo.exception.ApiResult;

import cn.smallmartial.demo.exception.BusinessException;

import cn.smallmartial.demo.service.TokenService;

import cn.smallmartial.demo.utils.RedisUtil;

import io.netty.util.internal.StringUtil;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

import java.util.Random;

import java.util.UUID;

/**

 * @Author smallmartial

 * @Date 2020/4/16

 * @Email smallmarital@qq.com

 */

@Service

public class TokenServiceImpl implements TokenService {

    @Autowired

    private RedisUtil redisService;

 /**

     * Create token

     *

     * @return

     */

    @Override

    public String createToken() {

        String str = UUID.randomUUID().toString().replace("-", "");

        StringBuilder token = new StringBuilder();

        try {

            token.append(RedisKeyPrefix.TOKEN_PREFIX).append(str);

            redisService.setEx(token.toString(), token.toString(), 10000L);

            boolean empty = StringUtils.isEmpty(token.toString());

 if (!empty) {

 return token.toString();

            }

        } catch (Exception ex) {

            ex.printStackTrace();

        }

 return null;

    }

 /**

     * Check token

     *

     * @param request

     * @return

     */

    @Override

    public boolean checkToken(HttpServletRequest request) throws Exception {

        String token = request.getHeader(RedisKeyPrefix.TOKEN_NAME);

 if (StringUtils.isEmpty(token)) {// token does not exist in header

            token = request.getParameter(RedisKeyPrefix.TOKEN_NAME);

 if (StringUtils.isEmpty(token)) {// There is no token in the parameter

                throw new BusinessException(ApiResult.BADARGUMENT);

            }

        }

 if (!redisService.exists(token)) {

            throw new BusinessException(ApiResult.REPETITIVE_OPERATION);

        }

        boolean remove = redisService.remove(token);

 if (!remove) {

            throw new BusinessException(ApiResult.REPETITIVE_OPERATION);

        }

 return true;

    }

}

Interceptor configuration

It is used to intercept the token of the front end and judge whether the token of the front end is valid

@Configuration

public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Bean

    public AuthInterceptor authInterceptor() {

 return new AuthInterceptor();

    }

 /**

     * Interceptor configuration

     *

     * @param registry

     */

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(authInterceptor());

//                .addPathPatterns("/ksb/**")

//                .excludePathPatterns("/ksb/auth/**", "/api/common/**", "/error", "/api/*");

        super.addInterceptors(registry);

    }

    @Override

    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("/**").addResourceLocations(

 "classpath:/static/");

        registry.addResourceHandler("swagger-ui.html").addResourceLocations(

 "classpath:/META-INF/resources/");

        registry.addResourceHandler("/webjars/**").addResourceLocations(

 "classpath:/META-INF/resources/webjars/");

        super.addResourceHandlers(registry);

    }

} 

Interceptor processor: mainly used to intercept scan to Autoldempotent to annotation method, and then call tokenService's checkToken method to check whether token is correct. If we catch the exception, render the abnormal information into json and return it to the front end. This part of the code is mainly linked to the user-defined annotation part. The main codes are as follows:

@Slf4j

public class AuthInterceptor extends HandlerInterceptorAdapter {

    @Autowired

    private TokenService tokenService;

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

 if (!(handler instanceof HandlerMethod)) {

 return true;

        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;

        Method method = handlerMethod.getMethod();

 //Scan marked by apiidempotent

        AutoIdempotent methodAnnotation = method.getAnnotation(AutoIdempotent.class);

 if (methodAnnotation != null) {

            try {

 return tokenService.checkToken(request);// Idempotency verification: if the verification passes, it will be released; if the verification fails, an exception will be thrown, and a friendly prompt will be returned through unified exception handling

            } catch (Exception ex) {

                throw new BusinessException(ApiResult.REPETITIVE_OPERATION);

            }

        }

 return true;

    }

} 

test case

The related test cases are used to simulate the business request class, and the related token is obtained through the related paths. Then the testidempotence method is called. This method annotates @Autoldempotent, and the interceptor will intercept all requests. When it is judged that there is a note on the processing method, the checkToken() method in TokenService will be called. If there is an exception, it will run out, and the code is as follows

/**

 * @Author smallmartial

 * @Date 2020/4/16

 * @Email smallmarital@qq.com

 */

@RestController

public class BusinessController {

    @Autowired

    private TokenService tokenService;

    @GetMapping("/get/token")

    public Object  getToken(){

        String token = tokenService.createToken();

 return ResponseUtil.ok(token) ;

    }

    @AutoIdempotent

    @GetMapping("/test/Idempotence")

    public Object testIdempotence() {

        String token = "Interface idempotency test";

 return ResponseUtil.ok(token) ;

    }

}

Access with browser

First access with the obtained token

Access again with the obtained token

You can see that the second access failed, that is, the idempotency verification passed.

Author:___ mySoul

summary

After knowing the interview key points of each large factory, you can improve the efficiency of question brushing and interview preparation. Next, Xiaobian also prepared the latest information of large Internet factories for you.

Data collection: I can get it for free by clicking

dempotence")

public Object testIdempotence() {

    String token = "Interface idempotency test";

return ResponseUtil.ok(token) ;

}

}

Access with browser

[External chain picture transfer...(img-gwN4VETs-1623583159777)]

Using the obtained token First visit

[External chain picture transfer...(img-WfaXrwMR-1623583159777)]


Using the obtained token Visit again

[External chain picture transfer...(img-r1uyNqH8-1623583159779)]


You can see that the second access failed, that is, the idempotency verification passed.

> Author:___mySoul 
## summary

After knowing the interview key points of each large factory, you can improve the efficiency of question brushing and interview preparation. Next, Xiaobian also prepared the latest information of large Internet factories for you.

> **[Data collection: I can get it for free by clicking](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)**

[External chain picture transfer...(img-oop07GdY-1623583159781)]

[External chain picture transfer...(img-4l2Txq88-1623583159781)]

[External chain picture transfer...(img-sgEWzCpJ-1623583159782)]

![Insert picture description here](https://img-blog.csdnimg.cn/img_convert/5817ced066f0cc9032533d791b056f10.png)

Keywords: Java Interview Programmer

Added by a94060 on Tue, 01 Feb 2022 07:59:35 +0200