主頁 >  其他 > 10分鐘精通 Redis分布式鎖中的各種門道

10分鐘精通 Redis分布式鎖中的各種門道

2020-12-21 11:20:30 其他

道阻且長,行則將至,請相信我,你一定會更優秀!

「 Redis 做分布式鎖,沒那么簡單,調整好心態,保證你有識訓」


本文我們主要聊 redis實作分布式鎖,別的不聊,先來三個問題熱熱身:

  1. 一個 setnx 就行了?還有人認為 incr 也可以?
  2. 再加個超時時間就行了?
  3. 你寫的分布式鎖,你確認你敢在生產環境用嗎?

熱身完畢,下邊我和你一起學習下 Redis分布式鎖到底該怎么玩?

1、為什么要有分布式鎖?

  • JUC提供的鎖機制,可以保證在同一個JVM行程中同一時刻只有一個執行緒執行操作邏輯;
  • 多服務多節點的情況下,就意味著有多個JVM行程,要做到這樣,就需要有一個中間人;
  • 分布式鎖就是用來保證在同一時刻,僅有一個JVM行程中的一個執行緒在執行操作邏輯;
  • 換句話說,JUC的鎖和分布式鎖都是一種保護系統資源的措施,盡可能將并發帶來的不確定性轉換為同步的確定性;

2、先捋脈絡,再想風險,最后再寫代碼

當我們設計一個東西的時候,很多同學腦子里想到的第一件事就是代碼,代碼,聽我說,你一定要先思考,要做一根能思想的葦草,代碼是死的,三思而后行,

所以,一定要先在腦子里想,這把鎖,我要用它干什么,它要保證什么,有沒有什么意外情況,會存在什么風險,先全域看一下,別一下子鉆到里邊,想完了之后,然后一定要落地,絕對不可以紙上談兵,自己一定要把代碼寫出來,自己去測驗,去解決問題,看到底行不行,只有寫出來,你才能驗證你的想法,“實踐是檢驗真理的唯一標準”,

為了保證文章的易讀性,接下來,我將采用理論 + 代碼的形式,從整體到部分,從宏觀到微觀,帶你全面看透 Redis分布式鎖,

3、一步一步,看透 Redis 分布式鎖中的門道

我們一起捋一下,很多執行緒去上鎖,誰鎖成功誰就有權利執行操作邏輯,其他執行緒要么直接走搶鎖失敗的邏輯,要么自旋嘗試搶鎖;

  • 比方說 A執行緒競爭到了鎖,開始執行操作邏輯(我的代碼邏輯演示中,使用 Jedis客戶端為例);
public static void doSomething() {
    // RedisLock是我封裝的一個類,后面會講到
    RedisLock redisLock = new RedisLock(jedis); // 創建jedis實體的代碼省略,不是重點
    try {
        redisLock.lock(); // 上鎖
        
        // 處理業務
        System.out.println(Thread.currentThread().getName() + " 執行緒處理業務邏輯中...");
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + " 執行緒處理業務邏輯完畢");
        
        redisLock.unlock(); // 釋放鎖
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • 正常情況下,A 執行緒執行完操作邏輯后,應該將鎖釋放,如果說執行程序中拋出例外,程式不再繼續走正常的釋放鎖流程,沒有釋放鎖怎么辦?所以我們想到:
  • 釋放鎖的流程一定要在 finally{} 塊中執行,當然,上鎖的流程一定要在 finally{} 對應的 try{} 塊中,否則 finally{} 就沒用了,如下:

public static void doSomething() {
    RedisLock redisLock = new RedisLock(jedis); // 創建jedis實體的代碼省略,不是重點
    try {
        redisLock.lock(); // 上鎖,必須在 try{}中
        
        // 處理業務
        System.out.println(Thread.currentThread().getName() + " 執行緒處理業務邏輯中...");
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + " 執行緒處理業務邏輯完畢");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        redisLock.unlock(); // 在finally{} 中釋放鎖
    }
}

3-1、放在 finally{} 塊中就行了嗎?

  • 如果在執行 try{} 中邏輯的時候,程式出現了 System.exit(0); 或者 finally{} 中執行例外,比方說連接不上 redis-server了;或者還未執行到 finally{}的時候,JVM行程掛掉了,服務宕機;這些情況都會導致沒有成功釋放鎖,別的執行緒一直拿不到鎖,怎么辦?如果我的系統因為一個節點影響,別的節點也都無法正常提供服務了,那我的系統也太弱了,所以我們想到必須要將風險降低,可以給鎖設定一個超時時間,比方說 1秒,即便發生了上邊的情況,那我的鎖也會在 1秒之后自動釋放,其他執行緒就可以獲取到鎖,接班干活了;
    	 public static final String lock_key = "haolin-lock";
     
         public void lock() {		
    		while (!tryLock()) {
    			try {
    				Thread.sleep(50); // 在while中自旋,如果說讀者想設定一些自旋次數,等待最大時長等自己去擴展,不是此處的重點
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		
    		System.out.println("執行緒:" + threadName + ",占鎖成功!★★★");
    	 }
     
     	 private boolean tryLock() {
    		SetParams setParams = new SetParams();
    		setParams.ex(1); // 超時時間1s
    		setParams.nx();  // nx
    		String response = jedis.set(lock_key, "", setParams); // 轉換為redis命令就是:set haolin-key "" ex 1 nx
    		return "OK".equals(response);
    	 }

    注意,上鎖的時候,設定key和設定超時時間這兩個操作要是原子性的,要么都執行,要么都不執行,

    Redis原生支持:

    // http://redis.io/commands/set.html
    SET key value [EX seconds] [PX milliseconds] [NX|XX]

    不要在代碼里邊分兩次呼叫:

    set k v
    exipre k time

    這是錯誤的,如果第一個命令執行成功后,第二條命令由于各種原因沒有執行,就出問題了,

3-2、鎖的超時時間該怎么計算?

  • 我們剛才假設的 1s是怎么計算的?這個時間該設多少合適呢?
  • 聽我說,鎖中的業務邏輯的執行時間,不能瞎寫,一般是我們在測驗環境進行多次測驗,然后在壓測環境多輪壓測之后,比方說計算出平均的執行時間是 200ms,鎖的超時時間放大3-5倍,比如這里我們設定為 1s,為啥要放大,因為如果鎖的操作邏輯中有網路 IO操作,線上的網路不會總一帆風順,我們要給網路抖動留有緩沖時間,這個時候有的同學有想法,那我設定的再大一些,給網路足夠充裕的時間,我就設定 10s、1min不是更安全嗎?請注意,不要鉆到這一個點里邊,你要顧全大局,多大算大?越大越好?無窮大?那不等于不設定超時時間嗎?同時,這個時間,你要想清楚,如果你設定 10s,果真發生了宕機,那意味著這 10s中間,你的這個分布式鎖的服務全部節點都是不可用的,這個和你的業務以及系統的可用性有掛鉤,你要去衡量,要慎重,

3-3、加個超時時間就行了嗎?

  • 繼續,如果說 A執行緒在執行操作邏輯的程序中,別的執行緒直接進行了釋放鎖的操作,是不是就出問題了?
  • 什么?別的執行緒沒有獲得鎖卻直接執行了釋放鎖??現在是 A執行緒上的鎖,那肯定只能 A執行緒釋放鎖呀!別的執行緒釋放鎖算怎么回事?聯想 ReentrantLock中的 isHeldByCurrentThread()方法,所以我們想到,必須在鎖上加個標記,只有上鎖的執行緒 A執行緒知道,相當于是一個密語,也就是說釋放鎖的時候,首先先把密語和鎖上的標記進行匹配,如果匹配不上,就沒有權利釋放鎖;
   private boolean tryLock() {
		SetParams setParams = new SetParams();
		setParams.ex(1); // 超時時間1s
		setParams.nx();  // nx
		String response = jedis.set(lock_key, "", setParams); // 轉換為redis命令就是:set haolin-key "" ex 1 nx
		return "OK".equals(response);
	}
  
    // 別的執行緒直接呼叫釋放鎖操作,分布式鎖崩潰!
 	public void unlock() {
		jedis.del(encode(lock_key));
		System.out.println("執行緒:" + threadName + " 釋放鎖成功!☆☆☆");
	}
 
 	private byte[] encode(String param) {
		return param.getBytes();
	}

3-4、這個密語value設定成什么呢?

  • 這是有門道的,跟著我的思路走,繼續,
  • 很多同學說設定成一個 UUID就行了,上鎖之前,在該執行緒代碼中生成一個 UUID,將這個作為秘鑰,存在鎖鍵的 value中,釋放鎖的時候,用這個進行校驗,因為只有上鎖的執行緒知道這個秘鑰,別的執行緒是不知道的,這個可行嗎,當然可行,
   String releaseLock_lua = "if redis.call(\"get\",KEYS[1]) == ARGV[1] \n" + 
				"then\n" + 
				"    return redis.call(\"del\", KEYS[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";
    
    private boolean tryLock(String uuid) {
		SetParams setParams = new SetParams();
		setParams.ex(1); // 超時時間1s
		setParams.nx();  // nx
		String response = jedis.set(lock_key, uuid, setParams); // 轉換為redis命令就是:set haolin-key "" ex 1 nx
		return "OK".equals(response);
	}
 
 	public void unlock(String uuid) {
		
		List<byte[]> keys = Arrays.asList(encode(lock_key));
		List<byte[]> args = Arrays.asList(encode(uuid));
           
           // 使用lua腳本,保證原子性
		long eval = (Long) jedis.eval(encode(releaseLock_lua), keys, args);
		if (eval == 1) {
			System.out.println("執行緒:" + threadName + " 釋放鎖成功!☆☆☆");
		} else {
			System.out.println("執行緒:" + threadName + " 釋放鎖失敗!該執行緒未持有鎖!!!");
		}
		
	}
 
 	private byte[] encode(String param) {
		return param.getBytes();
	}

為什么使用 lua腳本?

保證原子性,因為是兩個操作,如果分兩步那就是:

get k // 進行秘鑰 value的比對
del k // 比對成功后,洗掉k

如果第一步比對成功后,第二步還沒來得及執行的時候,鎖到期,然后緊接著別的執行緒獲取到鎖,里邊的 uuid已經變了,也就是說持有鎖的執行緒已經不是該執行緒了,此時再執行第二步的洗掉鎖操作,肯定是錯誤的了,

3-5、繼續,現在把思維先跳出來,想想?可重入怎么搞?

  • 作為一把鎖,我們在使用 synchronized、ReentrantLock的時候是不是有可重入性?
  • 那咱們這把分布式鎖該如何實作可重入呢?如果 A執行緒的鎖方法邏輯中呼叫了 x()方法,x()方法中也需要獲取這把鎖,按照這個邏輯,x()方法中的鎖應該重入進去即可,那是不是需要將剛才生成的這個 UUID秘鑰傳遞給 x()方法?怎么傳遞?引數?這就侵入業務代碼了,

3-6、能不侵入業務系統嗎?

  • 我們主要是想給上鎖的 A執行緒設定一個只有它自己知道的秘鑰,把思路時鐘往回撥,想想:
  • 執行緒本身的 id(Thread.currentThread().getId())是不是就是一個唯一標識呢?我們把秘鑰 value設定為執行緒的 id不就行了,
   String releaseLock_lua = "if redis.call(\"get\",KEYS[1]) == ARGV[1] \n" + 
				"then\n" + 
				"    return redis.call(\"del\", KEYS[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";
    String addLockLife_lua = "if redis.call(\"exists\", KEYS[1]) == 1\n" + 
				"then\n" + 
				"    return redis.call(\"expire\", KEYS[1], ARGV[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";
    	
     public void lock() {
             // 判斷是否可重入
		if (isHeldByCurrentThread()) {
			return;
		}
		
		while (!tryLock()) {
			try {
				Thread.sleep(50); // 自旋
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("執行緒:" + threadName + ",占鎖成功!★★★");
	}
 
   // 是否是當前執行緒占有鎖,同時將超時時間重新設定,這個很重要,同樣也是原子操作
 	private boolean isHeldByCurrentThread() {
		
		List<byte[]> keys = Arrays.asList(encode(lock_key));
		List<byte[]> args = Arrays.asList(encode(String.valueOf(threadId)), encode(String.valueOf(1)));
		
		long eval = (Long) jedis.eval(encode(addLockLife_lua), keys, args);
		return eval == 1;
	}
    
    private boolean tryLock(String uuid) {
		SetParams setParams = new SetParams();
		setParams.ex(1); // 超時時間1s
		setParams.nx();  // nx
		String response = jedis.set(lock_key, String.valueOf(threadId), setParams); // 轉換為redis命令就是:set haolin-key xxx ex 1 nx
		return "OK".equals(response);
	}
 
 	public void unlock(String uuid) {
		
		List<byte[]> keys = Arrays.asList(encode(lock_key));
		List<byte[]> args = Arrays.asList(encode(String.valueOf(threadId)));
           
           // 使用lua腳本,保證原子性
		long eval = (Long) jedis.eval(encode(releaseLock_lua), keys, args);
		if (eval == 1) {
			System.out.println("執行緒:" + threadName + " 釋放鎖成功!☆☆☆");
		} else {
			System.out.println("執行緒:" + threadName + " 釋放鎖失敗!該執行緒未持有鎖!!!");
		}
		
	}
 
 	private byte[] encode(String param) {
		return param.getBytes();
	}

3-7、Thread-Id 真能行嗎?

  • 不行,
  • 想想,我們說一個 Thread的id是唯一的,是在同一個 JVM行程中,是在一個作業系統中,也就是在一個機器中,而現實是,我們的部署是集群部署,多個實體節點,那意味著會存在這樣一種情況,S1機器上的執行緒上鎖成功,此時鎖中秘鑰 value是執行緒id=1,如果說同一時間 S2機器中,正好執行緒id=1的執行緒嘗試獲得這把鎖,比對秘鑰發現成功,結果也重入了這把鎖,也開始執行邏輯,此時,我們的分布式鎖崩潰!怎么解決?我們只需要在每個節點中維護不同的標識即可,怎么維護呢?應用啟動的時候,使用 UUID生成一個唯一標識 APP_ID,放在記憶體中,此時,我們的秘鑰 value這樣存即可:APP_ID+ThreadId
   // static變數,final修飾,加載在記憶體中,JVM行程生命周期中不變
   private static final String APP_ID = UUID.randomUUID().toString();
   
    String releaseLock_lua = "if redis.call(\"get\",KEYS[1]) == ARGV[1] \n" + 
				"then\n" + 
				"    return redis.call(\"del\", KEYS[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";
    String addLockLife_lua = "if redis.call(\"exists\", KEYS[1]) == 1\n" + 
				"then\n" + 
				"    return redis.call(\"expire\", KEYS[1], ARGV[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";
    	
     public void lock() {
             // 判斷是否可重入
		if (isHeldByCurrentThread()) {
			return;
		}
		
		while (!tryLock()) {
			try {
				Thread.sleep(50); // 自旋
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println("執行緒:" + threadName + ",占鎖成功!★★★");
	}
 
    // 是否是當前執行緒占有鎖,同時將超時時間重新設定,這個很重要,同樣也是原子操作
 	private boolean isHeldByCurrentThread() {
		
		List<byte[]> keys = Arrays.asList(encode(lock_key));
		List<byte[]> args = Arrays.asList(encode(APP_ID + String.valueOf(threadId)), encode(String.valueOf(1)));
		
		long eval = (Long) jedis.eval(encode(addLockLife_lua), keys, args);
		return eval == 1;
	}
    
    private boolean tryLock(String uuid) {
		SetParams setParams = new SetParams();
		setParams.ex(1); // 超時時間1s
		setParams.nx();  // nx
		String response = jedis.set(lock_key, APP_ID + String.valueOf(threadId), setParams); // 轉換為redis命令就是:set haolin-key xxx ex 1 nx
		return "OK".equals(response);
	}
 
 	public void unlock(String uuid) {
		
		List<byte[]> keys = Arrays.asList(encode(lock_key));
		List<byte[]> args = Arrays.asList(encode(APP_ID + String.valueOf(threadId)));
           
           // 使用lua腳本,保證原子性
		long eval = (Long) jedis.eval(encode(releaseLock_lua), keys, args);
		if (eval == 1) {
			System.out.println("執行緒:" + threadName + " 釋放鎖成功!☆☆☆");
		} else {
			System.out.println("執行緒:" + threadName + " 釋放鎖失敗!該執行緒未持有鎖!!!");
		}
		
	}
 
 	private byte[] encode(String param) {
		return param.getBytes();
	}

3-8、APP_ID + ThreadId 能解決下邊這個問題嗎?

  • 是不是覺得有點意思了?
  • 繼續聽我說,如果 A執行緒執行邏輯中間開啟了一個子執行緒執行任務,這個子執行緒任務中也需要重入這把鎖,因為子執行緒獲取到的執行緒 id不一樣,導致重入失敗,那意味著需要將這個秘鑰繼續傳遞給子執行緒,JUC中 InheritableThreadLocal 派上用場,但是感覺怪怪的,因為執行緒間傳遞的是父執行緒的 id,

「至于選擇哪種 value的方式,根據實際的系統設計 + 業務場景,選擇最合適的即可,沒有最好,只有最合適,」

3-9、豁然開朗?再來一招!

  • 再一次把思路時鐘往回撥,回撥到設定超時時間那里,我們預估鎖方法執行時間是 200ms,我們放大 5倍后,設定超時時間是 1s,假想一下,如果生產環境中,鎖方法中的 IO操作,極端情況下超時嚴重,比方說 IO就消耗了 2s,那就意味著,在這次 IO還沒有結束的時候,我這把鎖已經到期釋放掉了,就意味著別的執行緒趁虛而入,分布式鎖崩潰!

3-10、搞了半天,鎖還是崩潰了?

  • 跟著我的思路走,別放棄,
  • 再一次把思維從現在的框框里跳出來,想一想,我們要做的是一把分布式鎖,想要的目的是同一時刻只有一個執行緒持有鎖,作為服務而言,這個鎖現在不管是被哪個執行緒上鎖成功了,我服務應該保證這個執行緒執行的安全性,怎么辦?鎖續命,什么意思,一旦這把鎖出現了上鎖操作,就意味著這把鎖開始投入使用,這時我的服務中需要有一個 daemon執行緒定時去守護我的鎖的安全性,怎么守護?比如說鎖超時時間設定的是 1s,那么我這個定時任務是每隔 300ms去 redis服務端做一次檢查,如果我還持有,你就給我續命,就像 session會話的活躍機制一樣,看個例子,我上鎖時候超時時間設定的是 1s,實際方法執行時間是 3s,這中間我的定時執行緒每隔 300ms就會去把這把鎖的超時時間重新設定為 1s,每隔 300ms一次,成功將鎖續命成功,完美!
public class RedisLockIdleThreadPool {
    private String threadAddLife_lua = "if redis.call(\"exists\", KEYS[1]) == 1\n" + 
				"then\n" + 
				"    return redis.call(\"expire\", KEYS[1], ARGV[1])\n" + 
				"else\n" + 
				"    return 0\n" + 
				"end";

	private volatile ScheduledExecutorService scheduledThreadPool;
	
	public RedisLockIdleThreadPool() {
		
		if (scheduledThreadPool == null) {
			synchronized (this) {
				if (scheduledThreadPool == null) {
					scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
					
					scheduledThreadPool.scheduleAtFixedRate(() -> {
						addLife();
					}, 0, 300, TimeUnit.MILLISECONDS);
				}
			}
		}
	}
	
	private void addLife() {
            // ... 省略jedis的初始化程序
            
		List<byte[]> keys = Arrays.asList(RedisLock.lock_key.getBytes());
		List<byte[]> args = Arrays.asList(String.valueOf(1).getBytes());
		
		jedis.eval(threadAddLife_lua.getBytes(), keys, args);
	}
	
}

4、風險!主從部署引來的問題 <master - slave>

哨兵主從部署的時候,會存在一個風險問題,因為 Redis默認的主從復制是異步的,那很自然可以想到一個問題,極端情況下,如果剛往 master節點寫入一個分布式鎖,而這個指令流還沒有來得及同步給任意一個 slave節點,此時,master節點宕機,其中一個 slave被哨兵選舉為 master,此時是沒有這個鎖的,別的執行緒再次來獲取鎖,又獲取鎖成功了,當然,這個概率極低,但是我們必須得承認這個風險的存在,本文對這塊不探究太細,后面我會和大家專門聊聊 Redis集群的那些事,

從 Redis官方檔案上摘抄如下(https://redis.io/topics/replication):

Redis uses by default asynchronous replication, which being low latency and high performance, is the natural replication mode for the vast majority of Redis use cases. 
譯文:Redis默認使用異步復制,低延遲和高性能,絕大多數的Redis服務使用自然復制模式,

完工,我建議你合上螢屏,自己在腦子里重新過一遍,每一步都在做什么,為什么要做,解決什么問題,想清楚之后,一定要,一定要自己親手來一遍代碼,

【下集預告】10分鐘拿下zookeeper 分布式鎖各種門道,會比 redis優秀嗎?下期見,

努力改變自己和身邊人的生活,

特別希望本文可以對您有所幫助,轉載請注明出處,感謝大家留言討論交流,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/238022.html

標籤:其他

上一篇:初識tomcat

下一篇:選擇排序和陣列

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more