In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)05/31 Report--
Most people do not understand the knowledge of this article "how to use token mechanism to achieve interface auto-idempotency", so the editor summarizes the following contents, detailed contents, clear steps, and has a certain reference value. I hope you can get something after reading this article. Let's take a look at this "how to use token mechanism to achieve interface auto-idempotent" article.
What is idempotent interface?
Idempotency means that the results of only multiple operations are consistent. Some people here may have questions.
Q: why should the results of multiple operations be consistent? For example, when I query the data, I find out the same every time, even if I modify it, do I have to find out the same every time?
A: by multiple times, we mean multiple operations in the same request. This multiple operation may occur in the following situations:
The front end is submitted repeatedly. For example, this business process takes 2 seconds, within 2 seconds, I click the submit button 3 times in a row, if the non-idempotent interface, then the back end will process 3 times. If it is a query, it will naturally have no impact, because the query itself is an idempotent operation, but if it is added, only one record will be added, and three entries will be added three times in a row, which is obviously not possible.
Response timeout leads to request retry: in the process of mutual invocation of micro services, if the order service invokes the payment service, the payment service pays successfully, but the order service times out when receiving the information returned by the payment service. So the order service retries and requests the payment service. As a result, the payment service deducts the user's money again. If that were the case, users would have come with a machete.
How to design idempotent interface
After the above description, I believe you have a clear understanding of what interface idempotence and its importance. So how to design it? There are roughly the following options:
Database record status mechanism: query the status before each operation, and judge whether to continue the operation according to the state of the database record. For example, the order service invokes the payment service and queries the payment status of the order before each call, so as to avoid repeated operations.
Token mechanism: before requesting a business interface, request the token interface (which will put the generated token into the redis) to obtain a token, and then bring the token when requesting the business interface. Before performing the business operation, we first obtain the token carried in the request to see if the token is in the redis. If so, delete it. If the deletion is successful, the token verification passes and the business operation continues. If the token is not in the redis, it has been deleted, that is, the business operation has been performed, and the business operation is not allowed. The general process is as follows:
Other solutions: there are many other solutions for idempotent interface design, such as globally unique id, optimistic locking and so on. This article is mainly about the use of token mechanism, if you are interested, you can study it by yourself.
Using token Mechanism to realize automatic Idempotence of Interface
1. Pom.xml: mainly introduces redis-related dependencies
Org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 redis.clients jedis org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.apache.commons commons-lang3 org.json json 20190722
2. Application.yml: mainly configuring redis
Server: port: 6666spring: application: name: idempotent-api redis: host: 192.168.2.43 port: 6379
3. Business code:
Create a new enumeration that lists the commonly used returned information, as follows:
@ Getter@AllArgsConstructorpublic enum ResultEnum {REPEATREQUEST (405, "repeat request"), OPERATEEXCEPTION (406, "operation exception"), HEADERNOTOKEN (407, "request header does not carry token"), ERRORTOKEN (408, "token correct"); private Integer code; private String msg;}
Create a new JsonUtil and output json to the page when the request is abnormal:
Public class JsonUtil {private JsonUtil () {} public static void writeJsonToPage (HttpServletResponse response, String msg) {PrintWriter writer = null; response.setCharacterEncoding ("UTF-8"); response.setContentType ("text/html; charset=utf-8"); try {writer = response.getWriter (); writer.print (msg) } catch (IOException e) {} finally {if (writer! = null) writer.close ();} create a new RedisUtil to operate redis:@Componentpublic class RedisUtil {
Private RedisUtil () {}
Private static RedisTemplate redisTemplate
@ Autowired public void setRedisTemplate (@ SuppressWarnings ("rawtypes") RedisTemplate redisTemplate) {redisTemplate.setKeySerializer (new StringRedisSerializer ()); / / sets the instantiated object redisTemplate.setValueSerializer (new GenericJackson2JsonRedisSerializer ()) of serialized Value; RedisUtil.redisTemplate = redisTemplate;}
/ * set key-value to expire in timeout seconds * @ param key * @ param value * @ param timeout * / public static void setString (String key, String value, Long timeout) {redisTemplate.opsForValue () .set (key, value, timeout, TimeUnit.SECONDS);}
/ * set key-value * @ param key * @ param value * / public static void setString (String key, String value) {redisTemplate.opsForValue () .set (key, value);}
/ * get key-value * @ param key * @ return * / public static String getString (String key) {return (String) redisTemplate.opsForValue () .get (key);}
/ * determine whether key exists * @ param key * @ return * / public static boolean isExist (String key) {return redisTemplate.hasKey (key);}
/ * Delete key * @ param key * @ return * / public static boolean delKey (String key) {return redisTemplate.delete (key);}} create a new TokenUtil to generate and verify token: there is nothing to say about generating token. Here, it is generated directly with uuid for simplicity, and then put into redis. Verify token. If the user does not carry token, return false; directly. If you carry token but do not have this token in redis, it means it has been deleted, that is, it has been accessed, and false; is returned. If there is one in redis, but the token in redis is inconsistent with the token carried by the user, false; is returned. If this is the first visit, delete the token in redis, and then return true. Public class TokenUtil {
Private TokenUtil () {}
Private static final String KEY = "token"; private static final String CODE = "code"; private static final String MSG = "msg"; private static final String JSON = "json"; private static final String RESULT = "result"
/ * generate token and put it in redis * @ return * / public static String createToken () {String token = UUID.randomUUID () .toString (); RedisUtil.setString (KEY, token, 60L); return RedisUtil.getString (KEY);}
/ * verify token * @ param request * @ return * @ throws JSONException * / public static Map checkToken (HttpServletRequest request) throws JSONException {String headerToken = request.getHeader (KEY); JSONObject json = new JSONObject (); Map resultMap = new HashMap (); / / without token in the request header, return false if (StringUtils.isEmpty (headerToken)) {json.put (CODE, ResultEnum.HEADERNOTOKEN.getCode ()) directly Json.put (MSG, ResultEnum.HEADERNOTOKEN.getMsg ()); resultMap.put (RESULT, false); resultMap.put (JSON, json.toString ()); return resultMap;}
If (StringUtils.isEmpty (RedisUtil.getString (KEY) {/ / if there is no token in redis, it means that the access has been successful. Return false json.put (CODE, ResultEnum.REPEATREQUEST.getCode ()); json.put (MSG, ResultEnum.REPEATREQUEST.getMsg ()); resultMap.put (RESULT, false); resultMap.put (JSON, json.toString ()); return resultMap } else {/ / if there is token in redis, delete it. If deleted successfully, true will be returned. If deletion fails, false String redisToken = RedisUtil.getString (KEY); boolean result = false; if (! redisToken.equals (headerToken)) {json.put (CODE, ResultEnum.ERRORTOKEN.getCode ()); json.put (MSG, ResultEnum.ERRORTOKEN.getMsg ()) } else {result = RedisUtil.delKey (KEY); String msg = result? Null: ResultEnum.OPERATEEXCEPTION.getMsg (); json.put (CODE, 400); json.put (MSG, msg);} resultMap.put (RESULT, result); resultMap.put (JSON, json.toString ()); return resultMap } create a new annotation to mark the interface that needs to be idempotent: @ Target (ElementType.METHOD) @ Retention (RetentionPolicy.RUNTIME) public @ interface NeedIdempotent {} and then create an interceptor to intercept methods annotated with @ NeedIdempotent and perform automatic idempotency. Public class IdempotentInterceptor implements HandlerInterceptor {
@ Override public boolean preHandle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object object) throws JSONException {/ / intercept is not a method, directly release if (! (object instanceof HandlerMethod)) {return true;} HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod () / / if it is a method and has @ NeedIdempotent annotations, automatically idempotent if (method.isAnnotationPresent (NeedIdempotent.class)) {Map resultMap = TokenUtil.checkToken (httpServletRequest); boolean result = (boolean) resultMap.get ("result"); String json = (String) resultMap.get ("json"); if (! result) {JsonUtil.writeJsonToPage (httpServletResponse, json) } return result;} else {return true;}}
@ Override public void postHandle (HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Object ModelAndView modelAndView) {}
@ Override public void afterCompletion (HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Object o, Exception e) {}} then configure the interceptor into spring: @ Configurationpublic class InterceptorConfig implements WebMvcConfigurer {
@ Override public void addInterceptors (InterceptorRegistry registry) {registry.addInterceptor (idempotentInterceptor ()) .addPathPatterns ("/ * *");} @ Bean public IdempotentInterceptor idempotentInterceptor () {return new IdempotentInterceptor ();}
}
Finally, create a new controller and test it happily:
@ RestController@RequestMapping ("/ idempotent") public class IdempotentApiController {
@ NeedIdempotent @ GetMapping ("/ hello") public String hello () {return "are you ok?";}
GetMapping ("/ token") public String token () {return TokenUtil.createToken ();}}
Accessing / token does not require any verification. Accessing / hello will automatically idempotent. Each visit requires getting token first. A token cannot be used twice.
The above is the content of this article on "how to use token mechanism to achieve interface auto-idempotence". I believe we all have some understanding. I hope the content shared by the editor will be helpful to you. If you want to know more related knowledge, please pay attention to the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.