当前位置: 首页 > news >正文

公网怎么做网站视频网站直播怎么做的

公网怎么做网站,视频网站直播怎么做的,企业解决方案案例,国外的包装设计网站如何使用注解实现接口的幂等性校验 背景什么是幂等性为什么要实现幂等性校验如何实现接口的幂等性校验1. 数据库唯一主键2. 数据库乐观锁3. 防重 Token 令牌4. redis 如何将这几种方式都组装到一起结语 背景 最近在小组同学卷的受不了的情况下#xff0c;我决定换一个方向卷去… 如何使用注解实现接口的幂等性校验 背景什么是幂等性为什么要实现幂等性校验如何实现接口的幂等性校验1. 数据库唯一主键2. 数据库乐观锁3. 防重 Token 令牌4. redis 如何将这几种方式都组装到一起结语 背景 最近在小组同学卷的受不了的情况下我决定换一个方向卷去在算法上还是认命吧跟他们差距太大了 在最近一段时间偶然看到网上关于接口幂等性校验的文章在我一番思索下发现他们的实现原理各有不同而且每种实现原理各有不同加之最近恰好在学设计模式我就在想怎样利用设计模式让我们可以随意选择不同的实现方式。在此声明一下笔者仅仅是一个学生对于正式的业务流程开发并不太懂只是利用自己现有的知识储备打造一个让自己使用起来更方便的小demo 如果有大佬觉得哪儿有问题欢迎指出。 什么是幂等性 在数学领域中对于幂等性的解释是 f(f(x)) f(x) 即幂等元素x在函数f的多次作用下其效果和在f的一次作用下相同。在编程上可以理解为如果某个函数方法或接口被调用多次其行为结果和被调用一次相同则这种函数或接口就具有幂等性。 简单举个例子天然幂等性 假设对象Person中有个name属性有个 setName(String name){this.name name }的方法那这个方法就是天然幂等的哦你输入相同的“小明”参数不论你重复调用多少次都是将名字设置为“小明”其对对象Person的影响都是一样的。这就是天然幂等性。 非幂等性 还是拿对象Person举例子假设对象中有个age属性有个 increaseAge(){this.age; }方法我们按正常的步骤一次一次调用是不会有问题的如果调用者没有控制好逻辑一次流程重复调用好几次这时候影响效果和一次是有非常大区别代码编写者以为它只会调用一次结果出现了意外调用了很多次恰好方法不具有幂等性于是就会出现和预期不一样的效果。这个方法本身是不具备幂等性的我们可以修改这个方法让其传入一个标识符每一次重复的请求会有相同的标识符方法内部可以根据标识符查数据库是不是已经处理过如果处理过就不重复处理。这样方法就具备了幂等性。 更通俗一点就是 当在进行转账的时候我们分了两个系统来处理这个转账的流程 ①系统A负责收集转账人和接收人还有金额的信息然后传给系统B进行转账将控制逻辑留在系统A。 ②系统B读取系统A传过来的信息负责更改数据库的金额。如果操作成功就回复系统A成功如果失败就回复系统A失败。 ③系统A可以接受系统B操作成功或失败的回复但是我们知道系统A这个交易流程是有等待时间的如果等待超时它不确认是否是转账成功或失败于是系统A会重试调用直到得到一个明确的回复。 这是系统大致的交易流程。这个流程是有问题的系统B提供的操作接口不是幂等性的因为A会重复调用接口导致出现一个接口被同一个数据源发送相同数据切想要达到请求一次接口的效果的现象。 常见请求方式的幂等性 √ 满足幂等x 不满足幂等可能满足也可能不满足幂等根据实际业务逻辑有关 方法类型是否幂等描述Get√Get 方法用于获取资源。其一般不会也不应当对系统资源进行改变所以是幂等的。PostxPost 方法一般用于创建新的资源。其每次执行都会新增数据所以不是幂等的。Put_Put 方法一般用于修改资源。该操作则分情况来判断是不是满足幂等更新操作中直接根据某个值进行更新也能保持幂等。不过执行累加操作的更新是非幂等。Delete_Delete 方法一般用于删除资源。该操作则分情况来判断是不是满足幂等当根据唯一值进行删除时删除同一个数据多次执行效果一样。不过需要注意带查询条件的删除则就不一定满足幂等了。例如在根据条件删除一批数据后这时候新增加了一条数据也满足条件然后又执行了一次删除那么将会导致新增加的这条满足条件数据也被删除。 为什么要实现幂等性校验 在接口调用时一般情况下都能正常返回信息不会重复提交不过在遇见以下情况时可以就会出现问题如 前端重复提交表单 在填写一些表格时候用户填写完成提交很多时候会因网络波动没有及时对用户做出提交成功响应致使用户认为没有成功提交然后一直点提交按钮这时就会发生重复提交表单请求。用户恶意进行刷单 例如在实现用户投票这种功能时如果用户针对一个用户进行重复提交投票这样会导致接口接收到用户重复提交的投票信息这样会使投票结果与事实严重不符。接口超时重复提交 很多时候 HTTP 客户端工具都默认开启超时重试的机制尤其是第三方调用接口时候为了防止网络波动超时等造成的请求失败都会添加重试机制导致一个请求提交多次。消息进行重复消费 当使用 MQ 消息中间件时候如果发生消息中间件出现错误未及时提交消费信息导致发生重复消费。 使用幂等性最大的优势在于使接口保证任何幂等性操作免去因重试等造成系统产生的未知的问题。 如何实现接口的幂等性校验 网上流传最多的应该是四种方式去实现接口的幂等性校验接下来我们来一个个盘点。 1. 数据库唯一主键 方案描述 数据库唯一主键的实现主要是利用数据库中主键唯一约束的特性一般来说唯一主键比较适用于“插入”时的幂等性其能保证一张表中只能存在一条带该唯一主键的记录。 使用数据库唯一主键完成幂等性时需要注意的是该主键一般来说并不是使用数据库中自增主键而是使用分布式 ID 充当主键(或者使用其他算法生成的全局唯一的id)这样才能能保证在分布式环境下 ID 的全局唯一性。 适用操作 插入操作 删除操作 使用限制 需要生成全局唯一主键 ID 主要流程 ① 客户端执行创建请求调用服务端接口。 ② 服务端执行业务逻辑生成一个分布式 ID将该 ID 充当待插入数据的主键然后执数据插入操作运行对应的 SQL 语句。 ③ 服务端将该条数据插入数据库中如果插入成功则表示没有重复调用接口。如果抛出主键重复异常则表示数据库中已经存在该条记录返回错误信息到客户端。 2. 数据库乐观锁 方案描述 数据库乐观锁方案一般只能适用于执行“更新操作”的过程我们可以提前在对应的数据表中多添加一个字段充当当前数据的版本标识。这样每次对该数据库该表的这条数据执行更新时都会将该版本标识作为一个条件值为上次待更新数据中的版本标识的值。 适用操作 更新操作 使用限制 需要数据库对应业务表中添加额外字段 3. 防重 Token 令牌 方案描述 针对客户端连续点击或者调用方的超时重试等情况例如提交订单此种操作就可以用 Token 的机制实现防止重复提交。简单的说就是调用方在调用接口的时候先向后端请求一个全局 IDToken请求的时候携带这个全局 ID 一起请求Token 最好将其放到 Headers 中后端需要对这个 Token 作为 Key用户信息作为 Value 到 Redis 中进行键值内容校验如果 Key 存在且 Value 匹配就执行删除命令然后正常执行后面的业务逻辑。如果不存在对应的 Key 或 Value 不匹配就返回重复执行的错误信息这样来保证幂等操作。 适用操作 插入操作 更新操作 删除操作 使用限制 需要生成全局唯一 Token 串 需要使用第三方组件 Redis 进行数据效验 4. redis 方案描述 第四种是我觉着用着挺方便的但是实用性应该不大而且和第三种类似我们可以把接口名加请求参数通过算法生成一个全局唯一的id然后 存到redis中如果在一定时间请求多次我们就直接拒绝。 适用操作 插入操作 更新操作 删除操作 使用限制 需要使用第三方组件 Redis 进行数据效验 如何将这几种方式都组装到一起 我使用了Java自带的注解以及设计模式中的策略模式我们可以在注解中直接指定幂等性校验的方式当然也可以在配置文件中指定但是直接在注解中指定更加灵活。 但是由于最近时间比较忙天天被某些人卷很少有时间去完善目前只是实现了redis和防重 Token 令牌两种方式的。 以下是部分代码 自定义注解 package org.example.annotation;import java.lang.annotation.*;/*** author zrq*/ Retention(RetentionPolicy.RUNTIME) Target(ElementType.METHOD) Documented public interface RequestMany {/*** 策略* return*/String value() default ;/*** 过期时间* return*/long expireTime() default 0; } 定义切面 package org.example.aop;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.example.annotation.RequestMany; import org.example.factory.RequestManyStrategy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.DigestUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors;/*** author zrq* ClassName RequestManyValidationAspect* date 2023/11/22 9:14* Description TODO*/ Aspect Component public class RequestManyValidationAspect {Autowiredprivate MapString, RequestManyStrategy idempotentStrategies;Around(annotation(org.example.annotation.RequestMany))public Object validateIdempotent(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature methodSignature (MethodSignature) joinPoint.getSignature();Method method methodSignature.getMethod();RequestMany requestMany method.getAnnotation(RequestMany.class);String strategy requestMany.value(); // 获取注解中配置的策略名称Integer time (int)requestMany.expireTime(); // 获取注解中配置的策略名称if (!idempotentStrategies.containsKey(strategy)) {throw new IllegalArgumentException(Invalid idempotent strategy: strategy);}String key generateKey(joinPoint); // 根据方法参数等生成唯一的keyRequestManyStrategy idempotentStrategy idempotentStrategies.get(strategy);idempotentStrategy.validate(key, time);return joinPoint.proceed();}private String generateKey(ProceedingJoinPoint joinPoint) {// 获取类名String className joinPoint.getTarget().getClass().getSimpleName();// 获取方法名MethodSignature methodSignature (MethodSignature) joinPoint.getSignature();String methodName methodSignature.getMethod().getName();// 获取方法参数Object[] args joinPoint.getArgs();String argString Arrays.stream(args).map(Object::toString).collect(Collectors.joining(,));// 获取请求携带的 TokenHttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token request.getHeader(token);// 生成唯一的 keyString key className : methodName : argString : token;String md5Password DigestUtils.md5DigestAsHex(key.getBytes());return md5Password;}} 处理异常 package org.example.exception;/*** 运行时异常* author binbin.hou* since 0.0.1*/ public class RequestManyValidationException extends RuntimeException {public RequestManyValidationException() {}public RequestManyValidationException(String message) {super(message);}public RequestManyValidationException(String message, Throwable cause) {super(message, cause);}public RequestManyValidationException(Throwable cause) {super(cause);}public RequestManyValidationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {super(message, cause, enableSuppression, writableStackTrace);} } 模式工厂 package org.example.factory;import org.example.exception.RequestManyValidationException;/*** author zrq* ClassName RequestManyStrategy* date 2023/11/22 9:04* Description TODO*/ public interface RequestManyStrategy {void validate(String key, Integer time) throws RequestManyValidationException; } 模式实现01 package org.example.factory.impl;import org.example.exception.RequestManyValidationException; import org.example.factory.RequestManyStrategy; import org.example.utils.RedisCache; import org.springframework.stereotype.Component;import javax.annotation.Resource; import java.util.concurrent.TimeUnit;/*** author zrq* ClassName RedisIdempotentStrategy* date 2023/11/22 9:07* Description TODO*/ Component public class RedisIdempotentStrategy implements RequestManyStrategy {Resourceprivate RedisCache redisCache;Overridepublic void validate(String key, Integer time) throws RequestManyValidationException {if (redisCache.hasKey(key)) {throw new RequestManyValidationException(请求次数过多);} else {redisCache.setCacheObject(key,1, time, TimeUnit.MINUTES);}} } 模式实现02 package org.example.factory.impl;import org.example.exception.RequestManyValidationException; import org.example.factory.RequestManyStrategy; import org.example.utils.RedisCache; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest;/*** author zrq* ClassName TokenIdempotentStrategy* date 2023/11/22 9:13* Description TODO*/ Component public class TokenIdempotentStrategy implements RequestManyStrategy {Resourceprivate RedisCache redisCache;Overridepublic void validate(String key, Integer time) throws RequestManyValidationException {HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String token request.getHeader(token);if (token null || token.isEmpty()) {throw new RequestManyValidationException(未授权的token);}// 根据 key 和 token 执行幂等性校验boolean isDuplicateRequest performTokenValidation(key, token);if (!isDuplicateRequest) {throw new RequestManyValidationException(多次请求);}}private boolean performTokenValidation(String key, String token) {// 执行根据 Token 进行幂等性校验的逻辑// 这里可以使用你选择的合适的方法比如将 Token 存储到数据库或缓存中然后检查是否已存在String storedToken redisCache.getCacheObject(key);// 比较存储的 Token 和当前请求的 Token 是否一致return token.equals(storedToken);}} redisutil类 package org.example.utils;import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.BitFieldSubCommands; import org.springframework.data.redis.core.*; import org.springframework.stereotype.Component;import java.util.*; import java.util.concurrent.TimeUnit;SuppressWarnings(value { unchecked, rawtypes }) Component Slf4j public class RedisCache {Autowiredpublic RedisTemplate redisTemplate;Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 缓存基本的对象Integer、String、实体类等** param key 缓存的键值* param value 缓存的值*/public T void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/*** 缓存基本的对象Integer、String、实体类等** param key 缓存的键值* param value 缓存的值* param timeout 时间* param timeUnit 时间颗粒度*/public T void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 设置有效时间** param key Redis键* param timeout 超时时间* return true设置成功false设置失败*/public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}public boolean hasKey(final String key){return Boolean.TRUE.equals(redisTemplate.hasKey(key));}/*** 设置有效时间** param key Redis键* param timeout 超时时间* param unit 时间单位* return true设置成功false设置失败*/public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/*** 获得缓存的基本对象。** param key 缓存键值* return 缓存键值对应的数据*/public T T getCacheObject(final String key){ValueOperationsString, T operation redisTemplate.opsForValue();return operation.get(key);}/*** 删除单个对象** param key*/public boolean deleteObject(final String key){return redisTemplate.delete(key);}/*** 删除集合对象** param collection 多个对象* return*/public long deleteObject(final Collection collection){return redisTemplate.delete(collection);}/*** 缓存List数据** param key 缓存的键值* param dataList 待缓存的List数据* return 缓存的对象*/public T long setCacheList(final String key, final ListT dataList){Long count redisTemplate.opsForList().rightPushAll(key, dataList);return count null ? 0 : count;}/*** 获得缓存的list对象** param key 缓存的键值* return 缓存键值对应的数据*/public T ListT getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/*** 缓存Set** param key 缓存键值* param dataSet 缓存的数据* return 缓存数据的对象*/public T BoundSetOperationsString, T setCacheSet(final String key, final SetT dataSet){BoundSetOperationsString, T setOperation redisTemplate.boundSetOps(key);IteratorT it dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/*** 获得缓存的set** param key* return*/public T SetT getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/*** 缓存Map** param key* param dataMap*/public T void setCacheMap(final String key, final MapInteger, T dataMap){if (dataMap ! null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/*** 获得缓存的Map** param key* return*/public T MapString, T getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/*** 往Hash中存入数据** param key Redis键* param hKey Hash键* param value 值*/public T void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/*** 获取Hash中的数据** param key Redis键* param hKey Hash键* return Hash中的对象*/public T T getCacheMapValue(final String key, final String hKey){HashOperationsString, String, T opsForHash redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/*** 删除Hash中的数据** param key* param hkey*/public void delCacheMapValue(final String key, final String hkey){HashOperations hashOperations redisTemplate.opsForHash();hashOperations.delete(key, hkey);}/*** 获取多个Hash中的数据** param key Redis键* param hKeys Hash键集合* return Hash对象集合*/public T ListT getMultiCacheMapValue(final String key, final CollectionObject hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/*** 获得缓存的基本对象列表** param pattern 字符串前缀* return 对象列表*/public CollectionString keys(final String pattern){return redisTemplate.keys(pattern);}public Boolean sign(String key, int day, boolean sign) {return redisTemplate.opsForValue().setBit(key, day, sign);}public ListLong result(String key, int day, int num){ListLong result stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0));return result;}public Long bigCount(String key) {Long execute (Long)redisTemplate.execute((RedisCallback) cbk - cbk.bitCount(key.getBytes()));return execute;}public T void setCacheZSet(String name,T key, double num) {if (key ! null) {redisTemplate.opsForZSet().add(name,key,num);}}public T Long getCacheZSetRanking(String name,String key) {Long aLong null;if (key ! null) {aLong redisTemplate.opsForZSet().reverseRank(name, key);}return aLong;}public T Double getCacheZSetScore(String name,T key) {Double score null;if (key ! null) {score redisTemplate.opsForZSet().score(name, key);}return score;}public Set getCacheZSetLookTop(String name,int nums) {Set set null;if (name ! null) {set redisTemplate.opsForZSet().reverseRange(name, 0, nums);}return set;}public Long getCacheZSetSize(String key) {Long aLong null;if (key ! null) {aLong redisTemplate.opsForZSet().zCard(key);}return aLong;}public T Long deleteCacheZSet(String key, T value) {Long remove null;if (key ! null) {remove redisTemplate.opsForZSet().remove(key, value);}return remove;}public ListLong getBitMap(String key,Integer day) {ListLong bitFieldList (ListLong) redisTemplate.execute((RedisCallbackListLong) cbk- cbk.bitField(key.getBytes(), BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0)));return bitFieldList;}public Long incrExpire(String key, long time) {Long count redisTemplate.opsForValue().increment(key, 1);if (count ! null count 1) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return count;}public boolean removeList(String listName, Integer count, String value) {redisTemplate.opsForList().remove(listName,count,value);return true;} } 配置文件 如果要实现其他方式的话只需要实现下RequestManyStrategy模板方法然后编写自己的校验逻辑就可以。欢迎大佬指正错误。 以上代码已经上传到github 仓库地址点这里 结语 大学过的可真快转眼就大三了自己的技术还是不行跟别人的差距还有很大距离希望自己能在有限的时间里学到更多有用的知识同时也希望在明年的这个时候可以坐在办公室里敲代码。突然想到高中时中二的一句话“听闻少年二字应与平庸相斥”谁不希望这样呢奈何身边大佬太多现在只能追赶别人的脚步。。。
http://www.pierceye.com/news/38261/

相关文章:

  • 网站备案照片怎么弄电商网站建设属于研发费用吗
  • 网畅学校网站管理系统广州番禺发布网
  • 淄博 网站运营做网站 十万
  • wordpress 大站seo排名优化的方法
  • 成都快速建站模板四川建设厅网站查询
  • 建设银行 公户 该网站使用过期的wordpress禁用修正版
  • 天津市住房和城乡建设局网站做明信片的网站
  • 网站建设 有哪些费用wordpress主机配置
  • wordpress搭建网站教程中山网站建设怎么样
  • 重庆网站制作网络编程技术题库
  • 专业广州网站建设大宗贸易采购平台
  • 国内做服装趋势的网站怎么做网站教程
  • 网站的费用多少合适商标注册证查询
  • 有没有做微信的动态图网站网站建设发展制度
  • 服务器租用相关网站中英文网站建设大概多少钱
  • 网站导航栏wordpress需要php
  • 上海力晟建设工程有限公司网站网站建设seo优化内蒙
  • 有哪些招聘网站品牌网站建设有什么作用
  • 陕西建设部网站官网2345浏览器官网下载
  • 网站建设费一般是什么费用营销型网站建设方法
  • 哪个网站有老外教做蛋糕网站建设和优化内容最重要性
  • 全网软文推广无锡网站优化哪家好
  • 网站建设与管理实践建筑品牌网站
  • 做英雄联盟网站的图片素材网站怎么做图片动态图
  • app模板下载网站电子商务网站开发语言占比
  • 房子竣工验收在哪个网站查国外网站域名备案
  • 西安免费做网站公司东至县住房和城乡建设网站
  • 帝国cms网站建设c++可以做网站吗
  • 珠海做企业网站多少钱阿里云空间做网站
  • 凡科做网站好吗seo数据