0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

为什么要实现幂等性校验 如何实现接口的幂等性校验

jf_ro2CN3Fa 来源:芋道源码 2024-02-20 14:14 次阅读

背景

最近在小组同学卷的受不了的情况下,我决定换一个方向卷去,在算法上还是认命吧,跟他们差距太大了, 在最近一段时间偶然看到网上关于接口幂等性校验的文章,在我一番思索下,发现他们的实现原理各有不同而且每种实现原理各有不同,加之最近恰好在学设计模式,我就在想怎样利用设计模式让我们可以随意选择不同的实现方式。在此声明一下,笔者仅仅是一个学生,对于正式的业务流程开发并不太懂,只是利用自己现有的知识储备,打造一个让自己使用起来更方便的小demo, 如果有大佬觉得哪儿有问题,欢迎指出。

什么是幂等性

在数学领域中对于幂等性的解释是 f(f(x)) = f(x) 即幂等元素x在函数f的多次作用下,其效果和在f的一次作用下相同。在编程上可以理解为,如果某个函数(方法)或接口被调用多次其行为结果和被调用一次相同,则这种函数或接口就具有幂等性。简单举个例子,==天然幂等性==:

假设对象Person中有个name属性,有个

setName(Stringname){
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 方法用于获取资源。其一般不会也不应当对系统资源进行改变,所以是幂等的。
Post x Post 方法一般用于创建新的资源。其每次执行都会新增数据,所以不是幂等的。
Put _ Put 方法一般用于修改资源。该操作则分情况来判断是不是满足幂等,更新操作中直接根据某个值进行更新,也能保持幂等。不过执行累加操作的更新是非幂等。
Delete _ Delete 方法一般用于删除资源。该操作则分情况来判断是不是满足幂等,当根据唯一值进行删除时,删除同一个数据多次执行效果一样。不过需要注意,带查询条件的删除则就不一定满足幂等了。例如在根据条件删除一批数据后,这时候新增加了一条数据也满足条件,然后又执行了一次删除,那么将会导致新增加的这条满足条件数据也被删除。

为什么要实现幂等性校验

在接口调用时一般情况下都能正常返回信息不会重复提交,不过在遇见以下情况时可以就会出现问题,如:

前端重复提交表单:在填写一些表格时候,用户填写完成提交,很多时候会因网络波动没有及时对用户做出提交成功响应,致使用户认为没有成功提交,然后一直点提交按钮,这时就会发生重复提交表单请求。

用户恶意进行刷单:例如在实现用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,这样会使投票结果与事实严重不符。

接口超时重复提交:很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口时候,为了防止网络波动超时等造成的请求失败,都会添加重试机制,导致一个请求提交多次。

消息进行重复消费:当使用 MQ 消息中间件时候,如果发生消息中间件出现错误未及时提交消费信息,导致发生重复消费。

使用幂等性最大的优势在于使接口保证任何幂等性操作,免去因重试等造成系统产生的未知的问题。

如何实现接口的幂等性校验

网上流传最多的应该是四种方式去实现接口的幂等性校验,接下来我们来一个个盘点。

数据库唯一主键

「方案描述」 数据库唯一主键的实现主要是利用数据库中主键唯一约束的特性,一般来说唯一主键比较适用于“插入”时的幂等性,其能保证一张表中只能存在一条带该唯一主键的记录。使用数据库唯一主键完成幂等性时需要注意的是,该主键一般来说并不是使用数据库中自增主键,而是使用分布式 ID 充当主键(或者使用其他算法生成的全局唯一的id),这样才能能保证在分布式环境下 ID 的全局唯一性。

「适用操作:」 插入操作 删除操作

「使用限制:」 需要生成全局唯一主键 ID;

「主要流程:」 ① 客户端执行创建请求,调用服务端接口。② 服务端执行业务逻辑,生成一个分布式 ID,将该 ID 充当待插入数据的主键,然后执数据插入操作,运行对应的 SQL 语句。③ 服务端将该条数据插入数据库中,如果插入成功则表示没有重复调用接口。如果抛出主键重复异常,则表示数据库中已经存在该条记录,返回错误信息到客户端。

数据库乐观锁

「方案描述:」 数据库乐观锁方案一般只能适用于执行“更新操作”的过程,我们可以提前在对应的数据表中多添加一个字段,充当当前数据的版本标识。这样每次对该数据库该表的这条数据执行更新时,都会将该版本标识作为一个条件,值为上次待更新数据中的版本标识的值。「适用操作:」 更新操作

「使用限制:」 需要数据库对应业务表中添加额外字段;

防重 Token 令牌

「方案描述:」 针对客户端连续点击或者调用方的超时重试等情况,例如提交订单,此种操作就可以用 Token 的机制实现防止重复提交。简单的说就是调用方在调用接口的时候先向后端请求一个全局 ID(Token),请求的时候携带这个全局 ID 一起请求(Token 最好将其放到 Headers 中),后端需要对这个 Token 作为 Key,用户信息作为 Value 到 Redis 中进行键值内容校验,如果 Key 存在且 Value 匹配就执行删除命令,然后正常执行后面的业务逻辑。如果不存在对应的 Key 或 Value 不匹配就返回重复执行的错误信息,这样来保证幂等操作。

「适用操作:」 插入操作 更新操作 删除操作

「使用限制:」 需要生成全局唯一 Token 串;需要使用第三方组件 Redis 进行数据效验;

redis

「方案描述:」

第四种是我觉着用着挺方便的,但是实用性应该不大,而且和第三种类似,我们可以把接口名加请求参数通过算法生成一个全局唯一的id,然后 存到redis中,如果在一定时间请求多次,我们就直接拒绝。

「适用操作:」 插入操作 更新操作 删除操作

「使用限制:」 需要使用第三方组件 Redis 进行数据效验;

如何将这几种方式都组装到一起

我使用了Java自带的注解以及设计模式中的策略模式,我们可以在注解中直接指定幂等性校验的方式,当然也可以在配置文件中指定,但是直接在注解中指定更加灵活。

但是,由于最近时间比较忙,天天被某些人卷,很少有时间去完善,目前只是实现了redis和防重 Token 令牌两种方式的。以下是部分代码

「自定义注解」

packageorg.example.annotation;

importjava.lang.annotation.*;

/**
*@authorzrq
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public@interfaceRequestMany{
/**
*策略
*@return
*/
Stringvalue()default"";

/**
*过期时间
*@return
*/
longexpireTime()default0;
}

「定义切面」

packageorg.example.aop;

importorg.aspectj.lang.ProceedingJoinPoint;
importorg.aspectj.lang.annotation.Around;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.example.annotation.RequestMany;
importorg.example.factory.RequestManyStrategy;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.stereotype.Component;
importorg.springframework.util.DigestUtils;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;

importjavax.servlet.http.HttpServletRequest;
importjava.lang.reflect.Method;
importjava.util.Arrays;
importjava.util.Map;
importjava.util.stream.Collectors;

/**
*@authorzrq
*@ClassNameRequestManyValidationAspect
*@date2023/11/229:14
*@DescriptionTODO
*/
@Aspect
@Component
publicclassRequestManyValidationAspect{
@Autowired
privateMapidempotentStrategies;

@Around("@annotation(org.example.annotation.RequestMany)")
publicObjectvalidateIdempotent(ProceedingJoinPointjoinPoint)throwsThrowable{
MethodSignaturemethodSignature=(MethodSignature)joinPoint.getSignature();
Methodmethod=methodSignature.getMethod();

RequestManyrequestMany=method.getAnnotation(RequestMany.class);
Stringstrategy=requestMany.value();//获取注解中配置的策略名称
Integertime=(int)requestMany.expireTime();//获取注解中配置的策略名称
if(!idempotentStrategies.containsKey(strategy)){
thrownewIllegalArgumentException("Invalididempotentstrategy:"+strategy);
}
Stringkey=generateKey(joinPoint);//根据方法参数等生成唯一的key
RequestManyStrategyidempotentStrategy=idempotentStrategies.get(strategy);
idempotentStrategy.validate(key,time);
returnjoinPoint.proceed();
}

privateStringgenerateKey(ProceedingJoinPointjoinPoint){
//获取类名
StringclassName=joinPoint.getTarget().getClass().getSimpleName();

//获取方法名
MethodSignaturemethodSignature=(MethodSignature)joinPoint.getSignature();
StringmethodName=methodSignature.getMethod().getName();

//获取方法参数
Object[]args=joinPoint.getArgs();
StringargString=Arrays.stream(args)
.map(Object::toString)
.collect(Collectors.joining(","));
//获取请求携带的Token
HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
Stringtoken=request.getHeader("token");
//生成唯一的key
Stringkey=className+":"+methodName+":"+argString+":"+token;
Stringmd5Password=DigestUtils.md5DigestAsHex(key.getBytes());
returnmd5Password;
}

}

「处理异常」

packageorg.example.exception;

/**
*运行时异常
*@authorbinbin.hou
*@since0.0.1
*/
publicclassRequestManyValidationExceptionextendsRuntimeException{

publicRequestManyValidationException(){
}

publicRequestManyValidationException(Stringmessage){
super(message);
}

publicRequestManyValidationException(Stringmessage,Throwablecause){
super(message,cause);
}

publicRequestManyValidationException(Throwablecause){
super(cause);
}

publicRequestManyValidationException(Stringmessage,Throwablecause,booleanenableSuppression,booleanwritableStackTrace){
super(message,cause,enableSuppression,writableStackTrace);
}
}

「模式工厂」

packageorg.example.factory;

importorg.example.exception.RequestManyValidationException;

/**
*@authorzrq
*@ClassNameRequestManyStrategy
*@date2023/11/229:04
*@DescriptionTODO
*/
publicinterfaceRequestManyStrategy{
voidvalidate(Stringkey,Integertime)throwsRequestManyValidationException;
}

「模式实现01」

packageorg.example.factory.impl;

importorg.example.exception.RequestManyValidationException;
importorg.example.factory.RequestManyStrategy;
importorg.example.utils.RedisCache;
importorg.springframework.stereotype.Component;

importjavax.annotation.Resource;
importjava.util.concurrent.TimeUnit;

/**
*@authorzrq
*@ClassNameRedisIdempotentStrategy
*@date2023/11/229:07
*@DescriptionTODO
*/
@Component
publicclassRedisIdempotentStrategyimplementsRequestManyStrategy{
@Resource
privateRedisCacheredisCache;

@Override
publicvoidvalidate(Stringkey,Integertime)throwsRequestManyValidationException{
if(redisCache.hasKey(key)){
thrownewRequestManyValidationException("请求次数过多");
}else{
redisCache.setCacheObject(key,"1",time,TimeUnit.MINUTES);
}
}
}

「模式实现02」

packageorg.example.factory.impl;

importorg.example.exception.RequestManyValidationException;
importorg.example.factory.RequestManyStrategy;
importorg.example.utils.RedisCache;
importorg.springframework.data.redis.connection.RedisConnectionFactory;
importorg.springframework.data.redis.connection.jedis.JedisConnectionFactory;
importorg.springframework.data.redis.core.RedisTemplate;
importorg.springframework.data.redis.serializer.StringRedisSerializer;
importorg.springframework.stereotype.Component;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;

importjavax.annotation.Resource;
importjavax.servlet.http.HttpServletRequest;

/**
*@authorzrq
*@ClassNameTokenIdempotentStrategy
*@date2023/11/229:13
*@DescriptionTODO
*/
@Component
publicclassTokenIdempotentStrategyimplementsRequestManyStrategy{
@Resource
privateRedisCacheredisCache;
@Override
publicvoidvalidate(Stringkey,Integertime)throwsRequestManyValidationException{
HttpServletRequestrequest=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
Stringtoken=request.getHeader("token");
if(token==null||token.isEmpty()){
thrownewRequestManyValidationException("未授权的token");
}
//根据key和token执行幂等性校验
booleanisDuplicateRequest=performTokenValidation(key,token);
if(!isDuplicateRequest){
thrownewRequestManyValidationException("多次请求");
}
}
privatebooleanperformTokenValidation(Stringkey,Stringtoken){
//执行根据Token进行幂等性校验的逻辑
//这里可以使用你选择的合适的方法,比如将Token存储到数据库或缓存中,然后检查是否已存在
StringstoredToken=redisCache.getCacheObject(key);
//比较存储的Token和当前请求的Token是否一致
returntoken.equals(storedToken);
}
}

「redisutil类」

packageorg.example.utils;

importlombok.extern.slf4j.Slf4j;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.data.redis.connection.BitFieldSubCommands;
importorg.springframework.data.redis.core.*;
importorg.springframework.stereotype.Component;

importjava.util.*;
importjava.util.concurrent.TimeUnit;

@SuppressWarnings(value={"unchecked","rawtypes"})
@Component
@Slf4j
publicclassRedisCache
{
@Autowired
publicRedisTemplateredisTemplate;
@Autowired
privateStringRedisTemplatestringRedisTemplate;

/**
*缓存基本的对象,Integer、String、实体类等
*
*@paramkey缓存的键值
*@paramvalue缓存的值
*/
publicvoidsetCacheObject(finalStringkey,finalTvalue)
{
redisTemplate.opsForValue().set(key,value);
}

/**
*缓存基本的对象,Integer、String、实体类等
*
*@paramkey缓存的键值
*@paramvalue缓存的值
*@paramtimeout时间
*@paramtimeUnit时间颗粒度
*/
publicvoidsetCacheObject(finalStringkey,finalTvalue,finalIntegertimeout,finalTimeUnittimeUnit)
{
redisTemplate.opsForValue().set(key,value,timeout,timeUnit);
}

/**
*设置有效时间
*
*@paramkeyRedis键
*@paramtimeout超时时间
*@returntrue=设置成功;false=设置失败
*/
publicbooleanexpire(finalStringkey,finallongtimeout)
{
returnexpire(key,timeout,TimeUnit.SECONDS);
}
publicbooleanhasKey(finalStringkey)
{
returnBoolean.TRUE.equals(redisTemplate.hasKey(key));
}

/**
*设置有效时间
*
*@paramkeyRedis键
*@paramtimeout超时时间
*@paramunit时间单位
*@returntrue=设置成功;false=设置失败
*/
publicbooleanexpire(finalStringkey,finallongtimeout,finalTimeUnitunit)
{
returnredisTemplate.expire(key,timeout,unit);
}

/**
*获得缓存的基本对象。
*
*@paramkey缓存键值
*@return缓存键值对应的数据
*/
publicTgetCacheObject(finalStringkey)
{
ValueOperationsoperation=redisTemplate.opsForValue();
returnoperation.get(key);
}

/**
*删除单个对象
*
*@paramkey
*/
publicbooleandeleteObject(finalStringkey)
{
returnredisTemplate.delete(key);
}

/**
*删除集合对象
*
*@paramcollection多个对象
*@return
*/
publiclongdeleteObject(finalCollectioncollection)
{
returnredisTemplate.delete(collection);
}

/**
*缓存List数据
*
*@paramkey缓存的键值
*@paramdataList待缓存的List数据
*@return缓存的对象
*/
publiclongsetCacheList(finalStringkey,finalListdataList)
{
Longcount=redisTemplate.opsForList().rightPushAll(key,dataList);
returncount==null?0:count;
}

/**
*获得缓存的list对象
*
*@paramkey缓存的键值
*@return缓存键值对应的数据
*/
publicListgetCacheList(finalStringkey)
{
returnredisTemplate.opsForList().range(key,0,-1);
}

/**
*缓存Set
*
*@paramkey缓存键值
*@paramdataSet缓存的数据
*@return缓存数据的对象
*/
publicBoundSetOperationssetCacheSet(finalStringkey,finalSetdataSet)
{
BoundSetOperationssetOperation=redisTemplate.boundSetOps(key);
Iteratorit=dataSet.iterator();
while(it.hasNext())
{
setOperation.add(it.next());
}
returnsetOperation;
}

/**
*获得缓存的set
*
*@paramkey
*@return
*/
publicSetgetCacheSet(finalStringkey)
{
returnredisTemplate.opsForSet().members(key);
}

/**
*缓存Map
*
*@paramkey
*@paramdataMap
*/
publicvoidsetCacheMap(finalStringkey,finalMapdataMap)
{
if(dataMap!=null){
redisTemplate.opsForHash().putAll(key,dataMap);
}
}

/**
*获得缓存的Map
*
*@paramkey
*@return
*/
publicMapgetCacheMap(finalStringkey)
{
returnredisTemplate.opsForHash().entries(key);
}

/**
*往Hash中存入数据
*
*@paramkeyRedis键
*@paramhKeyHash键
*@paramvalue值
*/
publicvoidsetCacheMapValue(finalStringkey,finalStringhKey,finalTvalue)
{
redisTemplate.opsForHash().put(key,hKey,value);
}

/**
*获取Hash中的数据
*
*@paramkeyRedis键
*@paramhKeyHash键
*@returnHash中的对象
*/
publicTgetCacheMapValue(finalStringkey,finalStringhKey)
{
HashOperationsopsForHash=redisTemplate.opsForHash();
returnopsForHash.get(key,hKey);
}

/**
*删除Hash中的数据
*
*@paramkey
*@paramhkey
*/
publicvoiddelCacheMapValue(finalStringkey,finalStringhkey)
{
HashOperationshashOperations=redisTemplate.opsForHash();
hashOperations.delete(key,hkey);
}

/**
*获取多个Hash中的数据
*
*@paramkeyRedis键
*@paramhKeysHash键集合
*@returnHash对象集合
*/
publicListgetMultiCacheMapValue(finalStringkey,finalCollectionhKeys)
{
returnredisTemplate.opsForHash().multiGet(key,hKeys);
}

/**
*获得缓存的基本对象列表
*
*@parampattern字符串前缀
*@return对象列表
*/
publicCollectionkeys(finalStringpattern)
{
returnredisTemplate.keys(pattern);
}

publicBooleansign(Stringkey,intday,booleansign){
returnredisTemplate.opsForValue().setBit(key,day,sign);
}

publicListresult(Stringkey,intday,intnum){
Listresult=stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0)
);
returnresult;
}

publicLongbigCount(Stringkey){
Longexecute=(Long)redisTemplate.execute((RedisCallback)cbk->cbk.bitCount(key.getBytes()));
returnexecute;
}
publicvoidsetCacheZSet(Stringname,Tkey,doublenum){
if(key!=null){
redisTemplate.opsForZSet().add(name,key,num);
}
}
publicLonggetCacheZSetRanking(Stringname,Stringkey){
LongaLong=null;
if(key!=null){
aLong=redisTemplate.opsForZSet().reverseRank(name,key);
}
returnaLong;
}
publicDoublegetCacheZSetScore(Stringname,Tkey){
Doublescore=null;
if(key!=null){
score=redisTemplate.opsForZSet().score(name,key);
}
returnscore;
}
publicSetgetCacheZSetLookTop(Stringname,intnums){
Setset=null;
if(name!=null){
set=redisTemplate.opsForZSet().reverseRange(name,0,nums);
}
returnset;
}
publicLonggetCacheZSetSize(Stringkey){
LongaLong=null;
if(key!=null){
aLong=redisTemplate.opsForZSet().zCard(key);
}
returnaLong;
}

publicLongdeleteCacheZSet(Stringkey,Tvalue){
Longremove=null;
if(key!=null){
remove=redisTemplate.opsForZSet().remove(key,value);
}
returnremove;
}
publicListgetBitMap(Stringkey,Integerday){
ListbitFieldList=(List)redisTemplate.execute((RedisCallback>)cbk
->cbk.bitField(key.getBytes(),BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(day)).valueAt(0)));
returnbitFieldList;
}
publicLongincrExpire(Stringkey,longtime){
Longcount=redisTemplate.opsForValue().increment(key,1);
if(count!=null&&count==1){
redisTemplate.expire(key,time,TimeUnit.SECONDS);
}
returncount;
}
publicbooleanremoveList(StringlistName,Integercount,Stringvalue){
redisTemplate.opsForList().remove(listName,count,value);
returntrue;
}
}

「配置文件」

43e1f616-cf1d-11ee-a297-92fbcf53809c.jpg

如果要实现其他方式的话只需要实现下RequestManyStrategy模板方法,然后编写自己的校验逻辑就可以。

以上代码已经上传到github :https://github.com/Lumos-i/tools-and-frameworks

结语

大学过的可真快,转眼就大三了,自己的技术还是不行,跟别人的差距还有很大距离,希望自己能在有限的时间里学到更多有用的知识,同时也希望在明年的这个时候可以坐在办公室里敲代码。突然想到高中时中二的一句话“听闻少年二字,应与平庸相斥”,谁不希望这样呢,奈何身边大佬太多,现在只能追赶别人的脚步。。。

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8575

    浏览量

    151015
  • 数据库
    +关注

    关注

    7

    文章

    3794

    浏览量

    64360
  • Redis
    +关注

    关注

    0

    文章

    374

    浏览量

    10871

原文标题:策略模式实现接口的幂等性校验

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    离线计算中的和DataWorks中的相关事项

    比较容易。只需要设置导入的MaxCompute表的清洗规则为“写入前清理已有数据Insert Overwr”即可。这样数据在导入的过程中会先清空数据后再导入,从而实现
    发表于 02-27 13:24

    循环冗余校验码的单片机及CPLD 实现

    循环冗余码校验(CRC)是一种可靠很高的串行数据校验方法。介绍循环冗余码校验的基本原理,并分别用单片机和CPLD 作了循环冗余码校验的软件
    发表于 04-16 14:19 16次下载

    容错系统中的自校验技术及实现方法

    : 阐述了自校验技术在容错系统中的作用,给出了自校验网络实现原理及实现方法,指出用VHDL语言结合FPGA/CPLD是
    发表于 06-20 15:46 620次阅读
    容错系统中的自<b class='flag-5'>校验</b>技术及<b class='flag-5'>实现</b>方法

    LTE系统的CRC校验算法及DSP实现

    通过对两种常用CRC校验算法的研究分析,为TD-LTE测试仪表系统选择了一种最优的CRC校验算法,并在TMS320C64xDSP中实现。将CRC校验程序在CCS3.3中运行,其结果验证
    发表于 02-23 14:58 30次下载

    电量测量装置校验接口电路的实现方法

    :本文叙述在进行电量测量装置的高精度校验中,采用数字信号处理器TMS320F206及其与工业控制PC机(IPC)的ISA总线、双口SRAM、高精度A/D转换器接口电路的
    发表于 10-16 10:26 0次下载
    电量测量装置<b class='flag-5'>校验</b>中<b class='flag-5'>接口</b>电路的<b class='flag-5'>实现</b>方法

    在高并发下怎么保证接口

    前言 接口性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要的小伙伴一个参考。 不知道你有没有遇到过这些场景
    的头像 发表于 05-14 10:23 1804次阅读
    在高并发下怎么保证<b class='flag-5'>接口</b>的<b class='flag-5'>幂</b><b class='flag-5'>等</b><b class='flag-5'>性</b>?

    CRC校验原理及实现

    作者:王超首发:电子电路开发学习目录前言CRC算法简介CRC计算CRC校验CRC计算的C语言实现CRC计算工具总结前言最近的工作中,实现对通...
    发表于 01-26 17:37 30次下载
    CRC<b class='flag-5'>校验</b>原理及<b class='flag-5'>实现</b>

    什么是?关于接口的解决方案

    这里的乐观锁指的是用乐观锁的原理去实现,为数据字段增加一个version字段,当数据需要更新时,先去数据库里获取此时的version版本号
    发表于 10-09 10:19 1943次阅读

    分析解决)的方法

    这个概念,是一个数学上的概念,即:f……(f(f(x))) = f(x)。用在计算机领域,指的是系统里的接口或方法对外的一种承诺,使用相同参数对同一资源重复调用某个接口或方法的结果
    的头像 发表于 10-14 10:08 939次阅读

    Spring Boot实现接口的4种方案

    是一个数学与计算机学概念,在数学中某一元运算为时,其作用在任一元素两次后会和其作用一次的结果相同。
    的头像 发表于 11-08 10:21 997次阅读

    什么是实现原理

    在编程中一个操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。函数,或
    发表于 01-05 10:40 6119次阅读

    FPGA奇偶校验的基本原理及实现方法

    在数字电路中,数据的正确非常重要。为了保证数据的正确,在传输数据时需要添加一些冗余信息,以便在接收端进行校验。其中一种常用的校验方式是奇偶校验
    的头像 发表于 05-14 14:59 3023次阅读
    FPGA奇偶<b class='flag-5'>校验</b>的基本原理及<b class='flag-5'>实现</b>方法

    一个注解,优雅的实现接口

    除了查询和删除之外,还有更新操作,同样的更新操作在大多数场景下也是天然的,其例外是也会存在ABA的问题,更重要的是,比如执行update table set a = a + 1 where v = 1这样的更新就非等了。
    的头像 发表于 08-26 14:36 891次阅读
    一个注解,优雅的<b class='flag-5'>实现</b><b class='flag-5'>接口</b><b class='flag-5'>幂</b><b class='flag-5'>等</b><b class='flag-5'>性</b>!

    基于接口解决方案

    接口是指无论调用接口的次数是一次还是多次,对于同一资源的操作都只会产生一次结果。换句话说,多次重复调用相同的
    的头像 发表于 09-30 16:27 428次阅读
    基于<b class='flag-5'>接口</b><b class='flag-5'>幂</b><b class='flag-5'>等</b><b class='flag-5'>性</b>解决方案

    探索LabVIEW编程接口原理与实践

    原来是数学上的概念,在编程领域可以理解为:多次请求某一个资源或执行某一个操作时应该具有唯一同样结果,也就是说,其任意多次执行对资源
    的头像 发表于 02-29 10:24 610次阅读
    探索LabVIEW编程<b class='flag-5'>接口</b><b class='flag-5'>幂</b><b class='flag-5'>等</b><b class='flag-5'>性</b>原理与实践