Inhaltsverzeichnis
七、测试验证 " >七、测试验证
Heim Java JavaInterview Fragen Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

Aug 22, 2023 pm 03:57 PM
java面试题

Hallo zusammen, ich bin Bruder Tian

Als ich gestern ein Scheininterview für einen Freund führte, wie implementiert man Schnittstellen-Idempotenz? Aus dem Tonfall seiner Antwort ist ersichtlich, dass er den achtbeinigen Aufsatz auswendig lernt.

Damit jeder die idempotente Implementierung der Schnittstelle leicht erleben kann, hat Bruder Tian

diesen Artikel heute zusammengestellt .

Dieser Artikel hat insgesamt neun Hauptinhalte:

Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

1. Das Konzept der Idempotenz Imdempotenz ist für Laien ausgedrückt eine Schnittstelle, die dieselbe Anfrage mehrmals initiiert . Es muss sichergestellt sein, dass der Vorgang nur einmal ausgeführt werden kann, zum Beispiel:

  • Bestellschnittstelle, Bestellungen können nicht mehrfach erstellt werden
  • Zahlungsschnittstelle, wiederholte Zahlungen für die gleiche Bestellung können nur einmal abgebucht werden
  • Alipay-Rückrufschnittstelle, es kann zu mehreren Rückrufen kommen, wiederholte Rückrufe müssen bearbeitet werden
  • Gewöhnliche Schnittstelle zum Senden von Formularen. Aufgrund von Netzwerk-Timeouts und anderen Gründen können Sie nur mehrmals auf „Senden“ klicken und können nur einmal erfolgreich sein
    Einzigartiger Index – verhindert neue schmutzige Daten.

Token-Mechanismus – um wiederholte Seitenübermittlungen zu verhindern Basierend auf der Versionsnummer, beim Aktualisieren von Daten. Überprüfen Sie die Daten im Handumdrehen. Verteilte Sperre – Redis (jedis, redisson) oder Zookeeper-Implementierung.

  1. Zustandsmaschine – Zustandsänderung, beurteilen Sie den Status beim Aktualisieren von Daten
    3. Implementierung dieses Artikels
  2. Dieser Artikel verwendet die zweite Möglichkeit zur Implementierung, dh die Implementierung der Schnittstellen-Idempotenzüberprüfung durch den
  3. -Mechanismus.

    4. Implementierungsideen

    Erstellen Sie eine eindeutige Kennung für jede Anfrage, die Idempotenz gewährleisten musstoken, 先获取token, 并将此token存入redis, 请求接口时, 将此token放到header或者作为请求参数请求接口, 后端接口判断redis中是否存在此token:

    • Wenn vorhanden, verarbeiten Sie die Geschäftslogik normal und löschen Sie sie aus Redistoken, dann, wenn es sich um eine wiederholte Anfrage handelt, aufgrund von Token wurde gelöscht, es kann die Überprüfung nicht bestehen und gibt Bitte wiederholen Sie den Vorgang nichtPrompttoken, 那么, 如果是重复请求, 由于token已被删除, 则不能通过校验, 返回请勿重复操作提示
    • 如果不存在, 说明参数不合法或者是重复请求, 返回提示即可

    五、项目简介

    • Spring Boot
    • Redis
    • @ApiIdempotent注解 + 拦截器对请求进行拦截
    • @ControllerAdvice全局异常处理
    • 压测工具: Jmeter
    Wenn er nicht existiert, bedeutet dies, dass der Parameter ungültig ist oder es sich um eine wiederholte Anforderung handelt. Geben Sie einfach den zurück prompt

    5. Projekteinführung

    🎜Spring Boot🎜🎜🎜🎜Redis🎜🎜🎜🎜@ApiIdempotent Annotation + Interceptor fängt Anfragen ab🎜🎜🎜🎜@ControllerAdviceGlobale Ausnahmebehandlung🎜🎜🎜🎜Stresstest-Tool: Jmeter🎜🎜🎜🎜Erklärung:🎜🎜🎜Dieser Artikel konzentriert sich auf Die Kernimplementierung von Idempotence, wie Spring Boot funktioniert. Die Details zur Integration von Redis, ServerResponse, ResponseCode und anderen Details gehen über den Rahmen dieses Artikels hinaus.🎜

    六、代码实现

    1、maven依赖maven依赖

    <!-- Redis-Jedis -->
    <dependency>
       <groupId>redis.clients</groupId>
       <artifactId>jedis</artifactId>
       <version>2.9.0</version>
    </dependency>
    
    <!--lombok 本文用到@Slf4j注解, 也可不引用, 自定义log即可-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.10</version>
    </dependency>
    Nach dem Login kopieren

    2、JedisUtil

    @Component
    @Slf4j
    public class JedisUtil {
    
        @Autowired
        private JedisPool jedisPool;
    
        private Jedis getJedis() {
            return jedisPool.getResource();
        }
    
        /**
         * 设值
         *
         * @param key
         * @param value
         * @return
         */
        public String set(String key, String value) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.set(key, value);
            } catch (Exception e) {
                log.error("set key:{} value:{} error", key, value, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 设值
         *
         * @param key
         * @param value
         * @param expireTime 过期时间, 单位: s
         * @return
         */
        public String set(String key, String value, int expireTime) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.setex(key, expireTime, value);
            } catch (Exception e) {
                log.error("set key:{} value:{} expireTime:{} error", key, value, expireTime, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 取值
         *
         * @param key
         * @return
         */
        public String get(String key) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.get(key);
            } catch (Exception e) {
                log.error("get key:{} error", key, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 删除key
         *
         * @param key
         * @return
         */
        public Long del(String key) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.del(key.getBytes());
            } catch (Exception e) {
                log.error("del key:{} error", key, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 判断key是否存在
         *
         * @param key
         * @return
         */
        public Boolean exists(String key) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.exists(key.getBytes());
            } catch (Exception e) {
                log.error("exists key:{} error", key, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 设值key过期时间
         *
         * @param key
         * @param expireTime 过期时间, 单位: s
         * @return
         */
        public Long expire(String key, int expireTime) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.expire(key.getBytes(), expireTime);
            } catch (Exception e) {
                log.error("expire key:{} error", key, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        /**
         * 获取剩余时间
         *
         * @param key
         * @return
         */
        public Long ttl(String key) {
            Jedis jedis = null;
            try {
                jedis = getJedis();
                return jedis.ttl(key);
            } catch (Exception e) {
                log.error("ttl key:{} error", key, e);
                return null;
            } finally {
                close(jedis);
            }
        }
    
        private void close(Jedis jedis) {
            if (null != jedis) {
                jedis.close();
            }
        }
    
    }
    Nach dem Login kopieren

    3、自定义注解@ApiIdempotent

    /**
     * 在需要保证 接口幂等性 的Controller的方法上使用此注解
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiIdempotent {
    }
    Nach dem Login kopieren

    4、ApiIdempotentInterceptor 拦截器

    /**
     * 接口幂等性拦截器
     */
    public class ApiIdempotentInterceptor implements HandlerInterceptor {
    
        @Autowired
        private TokenService tokenService;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }
    
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
    
            ApiIdempotent methodAnnotation = method.getAnnotation(ApiIdempotent.class);
            if (methodAnnotation != null) {
                check(request);// 幂等性校验, 校验通过则放行, 校验失败则抛出异常, 并通过统一异常处理返回友好提示
            }
    
            return true;
        }
    
        private void check(HttpServletRequest request) {
            tokenService.checkToken(request);
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        }
    }
    Nach dem Login kopieren

    5、TokenServiceImpl

    @Service
    public class TokenServiceImpl implements TokenService {
    
        private static final String TOKEN_NAME = "token";
    
        @Autowired
        private JedisUtil jedisUtil;
    
        @Override
        public ServerResponse createToken() {
            String str = RandomUtil.UUID32();
            StrBuilder token = new StrBuilder();
            token.append(Constant.Redis.TOKEN_PREFIX).append(str);
    
            jedisUtil.set(token.toString(), token.toString(), Constant.Redis.EXPIRE_TIME_MINUTE);
    
            return ServerResponse.success(token.toString());
        }
    
        @Override
        public void checkToken(HttpServletRequest request) {
            String token = request.getHeader(TOKEN_NAME);
            if (StringUtils.isBlank(token)) {// header中不存在token
                token = request.getParameter(TOKEN_NAME);
                if (StringUtils.isBlank(token)) {// parameter中也不存在token
                    throw new ServiceException(ResponseCode.ILLEGAL_ARGUMENT.getMsg());
                }
            }
    
            if (!jedisUtil.exists(token)) {
                throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
            }
    
            Long del = jedisUtil.del(token);
            if (del <= 0) {
                throw new ServiceException(ResponseCode.REPETITIVE_OPERATION.getMsg());
            }
        }
    
    }
    Nach dem Login kopieren

    6、TestApplication

    @SpringBootApplication
    @MapperScan("com.wangzaiplus.test.mapper")
    public class TestApplication  extends WebMvcConfigurerAdapter {
    
        public static void main(String[] args) {
            SpringApplication.run(TestApplication.class, args);
        }
    
        /**
         * 跨域
         * @return
         */
        @Bean
        public CorsFilter corsFilter() {
            final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
            final CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.setAllowCredentials(true);
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
            return new CorsFilter(urlBasedCorsConfigurationSource);
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 接口幂等性拦截器
            registry.addInterceptor(apiIdempotentInterceptor());
            super.addInterceptors(registry);
        }
    
        @Bean
        public ApiIdempotentInterceptor apiIdempotentInterceptor() {
            return new ApiIdempotentInterceptor();
        }
    
    }
    Nach dem Login kopieren

    好了,以上便是代码的实现部分,下面我们就来验证一下。

    七、测试验证

    获取token的控制器TokenController

    @RestController
    @RequestMapping("/token")
    public class TokenController {
    
        @Autowired
        private TokenService tokenService;
    
        @GetMapping
        public ServerResponse token() {
            return tokenService.createToken();
        }
    
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    2、JedisUtil🎜
    @RestController
    @RequestMapping("/test")
    @Slf4j
    public class TestController {
    
        @Autowired
        private TestService testService;
    
        @ApiIdempotent
        @PostMapping("testIdempotence")
        public ServerResponse testIdempotence() {
            return testService.testIdempotence();
        }
    
    }
    Nach dem Login kopieren
    Nach dem Login kopieren
    🎜3、自定义注解@ApiIdempotent🎜rrreee🎜4、ApiIdempotentInterceptor  拦截器🎜rrreee🎜5、TokenServiceImpl< /code>🎜rrreee🎜6、<code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: 'Operator Mono', Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">TestApplication 🎜rrreee🎜好了,以上便是代码的实现部分,下面我们就来验证一下.🎜🎜🎜🎜🎜七、测试验证🎜🎜 🎜🎜🎜获取token的控制器TokenController :🎜
    @RestController
    @RequestMapping("/token")
    public class TokenController {
    
        @Autowired
        private TokenService tokenService;
    
        @GetMapping
        public ServerResponse token() {
            return tokenService.createToken();
        }
    
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    TestController, 注意@ApiIdempotent注解, 在需要幂等性校验的方法上声明此注解即可, 不需要校验的无影响:

    @RestController
    @RequestMapping("/test")
    @Slf4j
    public class TestController {
    
        @Autowired
        private TestService testService;
    
        @ApiIdempotent
        @PostMapping("testIdempotence")
        public ServerResponse testIdempotence() {
            return testService.testIdempotence();
        }
    
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    获取token

    Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

    查看Redis

    Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

    测试接口安全性: 利用Jmeter测试工具模拟50个并发请求, 将上一步获取到的token作为参数

    Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.
    Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.

    header或参数均不传token, 或者token值为空, 或者token值乱填, 均无法通过校验, 如token值为abcd

    8. Notizen (sehr wichtig) Es ist möglich, dass mehrere Threads gleichzeitig Zeile 46 erreichen. Zu diesem Zeitpunkt wurde das Token nicht gelöscht, sodass die Ausführung fortgesetzt wird. Wenn das Löschergebnis von
    nicht überprüft und direkt freigegeben wird, besteht weiterhin das Problem der wiederholten Übermittlung auftreten, auch wenn es sich eigentlich nur um einen echten Löschvorgang handelt, reproduzieren Sie ihn weiter unten. Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.
    Ändern Sie den Code leicht:

    Erneut anfordern

    Interviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.Schauen Sie sich noch einmal die Konsole an

    jedisUtil.del(token)

    Obwohl nur einer der Token tatsächlich gelöscht wird, da das Löschergebnis nicht überprüft wird, gibt es immer noch einen Daher müssen Parallelitätsprobleme überprüft werden

    9. Zusammenfassung

    Tatsächlich ist die Idee sehr einfach, das heißt, jede Anfrage ist garantiert einzigartig und wird somit 保证幂等性, 通过拦截器+注解, 就不用每次请求都写重复代码, 其实也可以利用Spring AOP erreicht.

    Okay, ich werde es heute hier teilen.

Das obige ist der detaillierte Inhalt vonInterviewer: In der Zahlungsschnittstelle kann bei wiederholten Zahlungen für dieselbe Bestellung nur einmal Geld abgebucht werden.. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Interviewer: Allgemeine Anmerkungen und Ausführungssequenz von Spring Aop Interviewer: Allgemeine Anmerkungen und Ausführungssequenz von Spring Aop Aug 15, 2023 pm 04:32 PM

Sie müssen Spring kennen, also lassen Sie uns über die Reihenfolge aller Benachrichtigungen von Aop sprechen. Wie wirkt sich Spring Boot oder Spring Boot 2 auf die Ausführungsreihenfolge von Aop aus? Erzählen Sie uns von den Fallstricken, auf die Sie bei AOP gestoßen sind?

Interview mit einer bestimmten Gruppe: Wie sollten Sie das Problem beheben, wenn Sie online auf OOM stoßen? Wie kann man es lösen? Welche Optionen? Interview mit einer bestimmten Gruppe: Wie sollten Sie das Problem beheben, wenn Sie online auf OOM stoßen? Wie kann man es lösen? Welche Optionen? Aug 23, 2023 pm 02:34 PM

OOM bedeutet, dass im Programm eine Sicherheitslücke vorliegt, die durch den Code oder die JVM-Parameterkonfiguration verursacht werden kann. In diesem Artikel erfahren die Leser, wie sie Fehler beheben können, wenn ein Java-Prozess OOM auslöst.

Anfänger können auch mit BAT-Interviewern konkurrieren: CAS Anfänger können auch mit BAT-Interviewern konkurrieren: CAS Aug 24, 2023 pm 03:09 PM

Das Extrakapitel der Java-Concurrent-Programming-Reihe, C A S (Compare and swap), ist nach wie vor in einem leicht verständlichen Stil mit Bildern und Texten gehalten und ermöglicht den Lesern eine verrückte Konversation mit dem Interviewer.

Die schriftlichen Testfragen von Ele.me scheinen einfach zu sein, aber sie verblüffen viele Leute Die schriftlichen Testfragen von Ele.me scheinen einfach zu sein, aber sie verblüffen viele Leute Aug 24, 2023 pm 03:29 PM

Unterschätzen Sie nicht die schriftlichen Prüfungsfragen vieler Unternehmen. Es gibt Fallstricke, in die Sie versehentlich geraten können. Wenn Sie auf eine solche schriftliche Testfrage zu Zyklen stoßen, empfehle ich Ihnen, ruhig zu denken und Schritt für Schritt vorzugehen.

5-String-Interviewfragen, weniger als 10 % der Leute können sie alle richtig beantworten! (mit Antwort) 5-String-Interviewfragen, weniger als 10 % der Leute können sie alle richtig beantworten! (mit Antwort) Aug 23, 2023 pm 02:49 PM

​In diesem Artikel werden fünf Interviewfragen zur Java-String-Klasse behandelt. Ich habe während des Interviewprozesses mehrere dieser fünf Fragen persönlich erlebt. Dieser Artikel wird Ihnen helfen zu verstehen, warum die Antworten auf diese Fragen so sind.

Letzte Woche hatte ich ein Vorstellungsgespräch bei XX Insurance und es war cool! ! ! Letzte Woche hatte ich ein Vorstellungsgespräch bei XX Insurance und es war cool! ! ! Aug 25, 2023 pm 03:44 PM

Letzte Woche ging ein Freund aus der Gruppe zu einem Interview mit Ping An Insurance. Das Ergebnis war etwas bedauerlich, aber ich hoffe, Sie lassen sich nicht entmutigen, im Grunde genommen alle Fragen, auf die Sie stoßen Das Interview kann durch Auswendiglernen der Interviewfragen gelöst werden, also arbeiten Sie bitte hart!

Meituan, sehen Sie, ob Sie darauf antworten können? Meituan, sehen Sie, ob Sie darauf antworten können? Aug 24, 2023 pm 03:51 PM

Meituan, sehen Sie, ob Sie darauf antworten können?

Es wird empfohlen, 100 Linux-Interviewfragen mit Antworten zu sammeln Es wird empfohlen, 100 Linux-Interviewfragen mit Antworten zu sammeln Aug 23, 2023 pm 02:37 PM

Dieser Artikel hat insgesamt mehr als 30.000 Wörter und umfasst Linux-Übersicht, Festplatte, Verzeichnis, Datei, Sicherheit, Syntaxebene, praktische Kämpfe, Dateiverwaltungsbefehle, Dokumentbearbeitungsbefehle, Festplattenverwaltungsbefehle, Netzwerkkommunikationsbefehle, Systemverwaltungsbefehle und Backup Komprimierungsbefehle usw. Abbau von Linux-Wissenspunkten.

See all articles