1. 程式人生 > 其它 >利用C#呼叫cmd執行OpenSSL申請私鑰

利用C#呼叫cmd執行OpenSSL申請私鑰

分散式鎖

概念

對某個操作上了鎖後,這個鎖對所有節點都有效,也就是保證所有節點互斥訪問。

傳統的鎖只對自己的節點有效。


基於Redis實現分散式鎖

原理

使用Redis的setnx命令,setnx在設定鍵值對的時候,先判斷鍵值對是否存在,存在返回0,不存在返回1,根據這個原理,需要加鎖的時候使用setnx設定一個lock,在解鎖之前,其他人上鎖lock會返回0,表示上鎖失敗,也就是這個鎖已經存在。在解鎖的時候使用del刪除這個鎖即可。這就達成了加鎖解鎖的目的。

產生的問題

如果上鎖後,上鎖的伺服器掛掉了,其他伺服器就沒辦法解鎖,就導致死鎖。

解決方案

上鎖後,再設定鎖的過期時間,過了期限這個鎖自動失效。

使用setnx和expire命令。

setnx <key> <value>

expire <key> <seconds>


方案的優化——原子操作

需要優化原因

這涉及到原子操作,若上鎖操作執行後,還沒設定過期時間,此時伺服器掛掉了,也會導致死鎖。

所以需要上鎖的同時設定過期時間。

解決方案

可以使用命令set <key><value>nx ex <time>


方案的繼續優化——UUID防止誤刪

需要優化原因

假設A上鎖後,做了操作,然後伺服器卡頓了,A的鎖過期後A還沒反應過來。

此時B上鎖做操作,還沒等到B解鎖,此時A反應過來進行解鎖,這時候解鎖解的是B的鎖。

 

解決方案

使用uuid表示不同的操作,首先做操作的時候自己隨機生成一個自己的uuid

set lock uuid nx ex 10

釋放鎖的時候,首先判斷自己的uuid和要釋放鎖的uuid是否一致

程式碼

@RestController
@RequestMapping("/redisTest")
public class RedisTestController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("testLock")
    public void testLock(){
        String uuid = UUID.randomUUID().toString();  //隨機生成當前操作的uuid
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,3, TimeUnit.SECONDS); //上鎖,lock儲存返回資訊
        if(lock){  //如果上鎖成功

//----------------------對資料操作------------------------------------------
            Object value = redisTemplate.opsForValue().get("num"); 
            if(StringUtils.isEmpty((String)value)) return; 
            int num = Integer.parseInt(value+"");     
            redisTemplate.opsForValue().set("num",++num);

//----------------------開始判定自己的uuid與鎖的uuid是否一致---------------------------------------
          String lockUuid=  (String) redisTemplate.opsForValue().get("lock");
          if(lockUuid.equals(uuid))         //如果uuid一致,則解鎖
            redisTemplate.delete("lock");
        }
        else{  //如果上鎖失敗,則等一等再試著上鎖
            try {
                Thread.sleep(100);
                testLock();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}


方案最終優化——LUA保證刪除的原子性

需要優化原因

A上鎖,做了具體操作,在正準備刪除的時候(比較了uuid,且一致),A剛好過期,此時B上鎖,A再刪除時刪除的是B的鎖。

解決方案

可以使用LUA指令碼保證刪除操作的原子性

程式碼

@RestController
@RequestMapping("/redisTest")
public class RedisTestController {
    @Autowired
    private RedisTemplate redisTemplate;

    @GetMapping("testLock")
    public void testLock(){
        String uuid = UUID.randomUUID().toString(); //隨機生成uuid
        String skuId= "25";  
        String locKey = "lock:"+skuId;

        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock",uuid,3, TimeUnit.SECONDS);
        if(lock){

//-----------------------------對資料操作--------------------------------------
            Object value = redisTemplate.opsForValue().get("num");
            if(StringUtils.isEmpty((String)value)) return;
            int num = Integer.parseInt(value+"");
            redisTemplate.opsForValue().set("num",++num);

//------------------------------刪除操作--------------------------------------
            String script="if redis.call('get',KEYS[1]) == ARGV[1] " +
                    "then return redis.call('del',KEYS[1]) else return 0 end";   //指令碼內容

            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
            redisScript.setScriptText(script);   //載入指令碼
            redisScript.setResultType(Long.class);
            redisTemplate.execute(redisScript, Arrays.asList(locKey),uuid);  //執行
        }
        else{
            try {
                Thread.sleep(100);
                testLock();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
}