色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

    文章

    8586

    瀏覽量

    151083
  • 數據庫
    +關注

    關注

    7

    文章

    3798

    瀏覽量

    64371
  • Redis
    +關注

    關注

    0

    文章

    374

    瀏覽量

    10872

原文標題:策略模式實現接口的冪等性校驗

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    離線計算中的和DataWorks中的相關事項

    比較容易。只需要設置導入的MaxCompute表的清洗規則為“寫入前清理已有數據Insert Overwr”即可。這樣數據在導入的過程中會先清空數據后再導入,從而實現
    發表于 02-27 13:24

    循環冗余校驗碼的單片機及CPLD 實現

    循環冗余碼校驗(CRC)是一種可靠很高的串行數據校驗方法。介紹循環冗余碼校驗的基本原理,并分別用單片機和CPLD 作了循環冗余碼校驗的軟件
    發表于 04-16 14:19 ?16次下載

    容錯系統中的自校驗技術及實現方法

    : 闡述了自校驗技術在容錯系統中的作用,給出了自校驗網絡實現原理及實現方法,指出用VHDL語言結合FPGA/CPLD是
    發表于 06-20 15:46 ?624次閱讀
    容錯系統中的自<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 ?1809次閱讀
    在高并發下怎么保證<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 ?1946次閱讀

    分析解決)的方法

    這個概念,是一個數學上的概念,即:f……(f(f(x))) = f(x)。用在計算機領域,指的是系統里的接口或方法對外的一種承諾,使用相同參數對同一資源重復調用某個接口或方法的結果
    的頭像 發表于 10-14 10:08 ?949次閱讀

    Spring Boot實現接口的4種方案

    是一個數學與計算機學概念,在數學中某一元運算為時,其作用在任一元素兩次后會和其作用一次的結果相同。
    的頭像 發表于 11-08 10:21 ?998次閱讀

    什么是實現原理

    在編程中一個操作的特點是其任意多次執行所產生的影響均與一次執行的影響相同。函數,或
    發表于 01-05 10:40 ?6125次閱讀

    FPGA奇偶校驗的基本原理及實現方法

    在數字電路中,數據的正確非常重要。為了保證數據的正確,在傳輸數據時需要添加一些冗余信息,以便在接收端進行校驗。其中一種常用的校驗方式是奇偶校驗
    的頭像 發表于 05-14 14:59 ?3034次閱讀
    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 ?896次閱讀
    一個注解,優雅的<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 ?431次閱讀
    基于<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 ?611次閱讀
    探索LabVIEW編程<b class='flag-5'>接口</b><b class='flag-5'>冪</b><b class='flag-5'>等</b><b class='flag-5'>性</b>原理與實踐
    主站蜘蛛池模板: 国产午夜不卡| www.99在线| 亚洲一区在线观看视频| 午夜福利免费0948视频| 涩涩游戏盒| 婷婷综合久久狠狠色| 色宅男午夜电影网站| 四虎永久在线精品国产免费| 日韩精品特黄毛片免费看| 日本吃孕妇奶水免费观看| 秋霞鲁丝片Av无码| 日本xxxxxx片免费播放18| 日本老师xxxxx18| 色综合久久五月| 无码免费视频AAAAAA片草莓| 午夜A级理论片左线播放| 亚洲 欧美 国产 综合 播放| 亚洲成人在线免费观看| 亚洲伊人精品| 在线视频a| 99热在线播放| 东北疯狂xxxxbbbb中国| 国产精品99久久久久久宅男AV| 国产三级影院| 久操久操久操| 美女被爽cao免费漫画| 秋霞在线观看视频一区二区三区| 日本特殊精油按摩| 羞羞漫画在线播放| 用快播看av的网站| av在线观看地址| 国产51麻豆二区精品AV视频| 国产人妖一区二区| 久久久久久久久女黄9999| 欧美精品色视频| 翁止熄痒禁伦短文合集免费视频| 亚洲精品久久久久久久蜜臀老牛| 中国xxxxx69| 成人免费视频一区| 好湿好紧水多AAAAA片秀人网| 久久人妻AV一区二区软件|