主頁 >  其他 > Spring Boot基于zookeeper原生方式實作分布式鎖

Spring Boot基于zookeeper原生方式實作分布式鎖

2021-10-28 07:27:57 其他

目錄

    • 一、背景
    • 二、maven依賴
    • 三、配置
      • 3.1、application.yml配置
      • 3.2、屬性配置類
      • 3.3、ZookeeperConfig配置件
    • 四、實戰
      • 4.1、介面
      • 4.2、介面核心實作
      • 4.3、測驗類
      • 4.4、結果
      • 4.5、關于CountDownLatch
    • 結語

一、背景

??我在之前的文章SpringBoot基于Zookeeper和Curator實作分布式鎖并分析其原理詳細介紹了它的使用及其原理,現在我們也根據這個思路,用zookeeper原生的方式來實作一個分布式鎖,加深對分布式鎖的理解,本文中Spring Boot的版本是2.5.2zookeeper的版本是3.6.3

??我們大致的大致的流程圖如下圖,可作為我們查看代碼的一個思路,不然看的頭大,(當然本圖是沒有包含可重入鎖的流程判斷在里面的
在這里插入圖片描述

二、maven依賴

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.alian</groupId>
    <artifactId>zklock</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>zklock</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.2</version>
        </dependency>

        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.6.3</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
		
		<!--主要用于Maps.newConcurrentMap()-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.14</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

三、配置

3.1、application.yml配置

application.yml

server:
  port: 8082
  servlet:
    context-path: /zklock

app:
  zookeeper:
    server: 10.130.3.16:2181
    session-timeout: 15000
    #這里配置的路徑沒有用"/"結尾
    root-lock-path: /root/alian

3.2、屬性配置類

??此配置類不懂的可以參考我另一篇文章:Spring Boot讀取組態檔常用方式

AppProperties.java

package com.alian.zklock.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "app.zookeeper")
public class AppProperties {

    /**
     * zookeeper服務地址
     */
    private String server;

    /**
     * session超時時間
     */
    private int sessionTimeout;

    /**
     * 分布式鎖路徑
     */
    private String rootLockPath;

}

3.3、ZookeeperConfig配置件

ZookeeperConfig.java

package com.alian.zklock.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooKeeper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.CountDownLatch;

@Slf4j
@Configuration
public class ZookeeperConfig {

    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    @Autowired
    private AppProperties appProperties;

    @Bean
    public ZooKeeper zookeeper() throws Exception {
        ZooKeeper zookeeper = new ZooKeeper(appProperties.getServer(), appProperties.getSessionTimeout(), event -> {
            log.info("Receive watched event: {}", event.getState());
            //獲取事件的狀態
            KeeperState keeperState = event.getState();
            //獲取時間型別
            EventType eventType = event.getType();
            //如果是建立連接
            if (KeeperState.SyncConnected == keeperState) {
                if (EventType.None == eventType) {
                    //如果建立連接成功,則發送信號量,讓后續阻塞程式向下執行
                    countDownLatch.countDown();
                    log.info("zookeeper建立連接");
                }
            }
        });
        //進行阻塞,當執行countDownLatch.countDown();后續代碼才會進行
        countDownLatch.await();
        return zookeeper;
    }

}

??這里主要是對ZooKeeper 進行連接配置,關于CountDownLatch的使用,本文最后有相關的介紹,

四、實戰

??定義了兩個方法:加鎖和釋放鎖,

4.1、介面

ILockService.java

package com.alian.zklock.service;

import java.util.concurrent.TimeUnit;

public interface ILockService {

    /**
     * 加鎖
     *
     * @param lockPath
     * @param time
     * @param unit
     * @return
     */
    boolean lock(String lockPath, long time, TimeUnit unit);

    /**
     * 釋放鎖
     *
     * @return
     */
    void release();

}

4.2、介面核心實作

??這個實作類的注釋,我想已經很詳細了,可以細細閱讀,可以加深你對zookeeper分布式鎖實作原理的理解,

ZookeeperLockService.java

package com.alian.zklock.service.impl;

import com.alian.zklock.service.ILockService;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
public class ZookeeperLockService implements ILockService {

	//依賴需要匯入:<groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version>
    private final ConcurrentMap<Thread, LockData> threadData = Maps.newConcurrentMap();

    @Autowired
    private ZooKeeper zooKeeper;

    //好的思想直接拿來用
    private static class LockData {
        final Thread owningThread;
        final String lockPath;
        final AtomicInteger lockCount = new AtomicInteger(1);
		//構造方法
        private LockData(Thread owningThread, String lockPath) {
            this.owningThread = owningThread;
            this.lockPath = lockPath;
        }
    }

    /**
     * 加鎖
     *
     * @param lockPath
     * @return
     * @throws Exception
     */
    public boolean lock(String lockPath, long time, TimeUnit unit) {
        //可重入,確保同一執行緒,可以重復加鎖
        Thread currentThread = Thread.currentThread();
        //根據執行緒號獲取執行緒鎖資料
        LockData lockData = threadData.get(currentThread);
        if (lockData != null) {
            // 說明該執行緒已加鎖過,直接放行
            lockData.lockCount.incrementAndGet();
            return true;
        }
        String currentLockPath = attemptLock(lockPath, time, unit);
        //如果不為空則表示獲取到了鎖
        if (StringUtils.isNotBlank(currentLockPath)) {
            //把資料快取起來
            LockData newLockData = new LockData(currentThread, currentLockPath);
            threadData.put(currentThread, newLockData);
            return true;
        }
        return false;
    }

    /**
     * 嘗試獲取鎖,獲取成功回傳鎖路徑
     *
     * @param lockPath
     * @param time
     * @param unit
     * @return
     */
    public String attemptLock(String lockPath, long time, TimeUnit unit) {
        //創建臨時有序節點,傳入的lockPath沒有"/"
        try {
            String currentLockPath = zooKeeper.create(lockPath + "/", "0".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            log.info("執行緒:【{}】->【{}】嘗試競爭鎖", Thread.currentThread().getName(), currentLockPath);
            //創建臨時節點失敗
            if (StringUtils.isBlank(currentLockPath)) {
                throw new Exception("生成臨時節點例外");
            }
            //檢查當前節點是否獲取到了鎖
            boolean hasLock = checkLocked(lockPath, currentLockPath, time, unit);
            //獲取到了鎖則回傳鎖節點路徑
            return hasLock ? currentLockPath : null;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 檢查是否獲取到鎖
     *
     * @param lockPath
     * @param currentLockPath
     * @param time
     * @param unit
     * @return
     * @throws Exception
     */
    public boolean checkLocked(String lockPath, String currentLockPath, long time, TimeUnit unit) {
        boolean hasLock = false;
        boolean toDelete = false;
        try {
            while (!hasLock) {
                //檢查是否獲取到了鎖,沒有獲取到則回傳前一個節點
                Pair<Boolean, String> pair = getsTheLock(lockPath, currentLockPath);
                //當前節點是否獲取到了鎖
                boolean currentLock = pair.getLeft();
                //獲取前一個節點
                String preSequencePath = pair.getRight();
                if (currentLock) {
                    //獲取到了鎖
                    hasLock = true;
                } else {
                    //等待
                    final CountDownLatch latch = new CountDownLatch(1);
                    //訂閱比自己次小順序節點的洗掉事件
                    Watcher watcher = watchedEvent -> {
                        log.info("監聽到的變化【】 watchedEvent = {}", watchedEvent);
                        latch.countDown();
                    };
                    Stat stat = zooKeeper.exists(preSequencePath, watcher);
                    if (stat != null) {
                        log.info("執行緒:【{}】等待鎖【{}】釋放", Thread.currentThread().getName(), preSequencePath);
                        boolean await = latch.await(time, unit);
                        if (!await) {
                            //說明超時了
                            log.info("獲取鎖超時");
                            toDelete = true;
                            break;
                        }
                    }
                    //檢查鎖
                    Pair<Boolean, String> checkPair = getsTheLock(lockPath, currentLockPath);
                    if (checkPair.getLeft()) {
                        hasLock = true;
                    }
                }
            }
        } catch (Exception e) {
            log.error("檢查是否獲取到鎖例外", e);
            if (e instanceof InterruptedException) {
                toDelete = true;
            }
        } finally {
            if (toDelete) {
                deleteCurrentPath(currentLockPath);
            }
        }
        return hasLock;
    }

    /**
     * 檢測是否已經獲取到了鎖,沒有獲取到則回傳前一個節點
     *
     * @param lockPath
     * @param currentLock
     * @return
     * @throws Exception
     */
    private Pair<Boolean, String> getsTheLock(String lockPath, String currentLock) throws Exception {
        //獲取根節點下所有子節點,不能用/結尾
        List<String> childrenList = zooKeeper.getChildren(lockPath, false);
        //節點按照編號,升序排列
        Collections.sort(childrenList);
        //如果是第一個,代表自己已經獲得了鎖
        String currentLockNode = currentLock.substring(currentLock.lastIndexOf("/") + 1);
        if (currentLockNode.equals(childrenList.get(0))) {
            log.info("節點【{}】成功的獲取分布式鎖", currentLock);
            return Pair.of(true, "");
        }
        //判斷自己排第幾個,回傳的是物件所在串列的序號
        int index = Collections.binarySearch(childrenList, currentLockNode);
        if (index < 0) { // 網路抖動,獲取到的子節點串列里可能已經沒有自己了
            throw new Exception("節點沒有找到: " + currentLockNode);
        }
        //如果沒有獲得鎖,則要監聽前一個節點
        String preSequencePath = lockPath + "/" + childrenList.get(index - 1);
        //回傳監聽的前一個節點
        return Pair.of(false, preSequencePath);
    }

    /**
     * 洗掉當前獲取鎖的節點
     *
     * @param currentLockPath
     */
    private void deleteCurrentPath(String currentLockPath) {
        try {
            //判斷路徑是否存在
            Stat stat = zooKeeper.exists(currentLockPath, false);
            if (stat != null) {
                //存在則洗掉
                zooKeeper.delete(currentLockPath, -1);
            }
        } catch (InterruptedException | KeeperException e) {
            log.error("洗掉節點例外");
        }
    }

    @Override
    public void release() {
        //獲取當前執行緒
        Thread currentThread = Thread.currentThread();
        //獲取當前執行緒的資料
        LockData lockData = threadData.get(currentThread);
        if (lockData == null) {
            throw new IllegalMonitorStateException("You do not own the lock: ");
        }
        //鎖計數器減1
        int newLockCount = lockData.lockCount.decrementAndGet();
        if (newLockCount > 0) {
            //可重入鎖,暫時不擅長節點
            return;
        }
        if (newLockCount < 0) {
            throw new IllegalMonitorStateException("Lock count has gone negative for lock: ");
        }
        try {
            //洗掉節點
            zooKeeper.delete(lockData.lockPath, -1);
            log.info("執行緒:【{}】釋放鎖【{}】", Thread.currentThread().getName(), lockData.lockPath);
        } catch (InterruptedException | KeeperException e) {
            e.printStackTrace();
        } finally {
            threadData.remove(currentThread);
        }
    }

}

4.3、測驗類

??我們為了方便檢驗我們的分布式鎖,初始化庫存為100,就使用3個執行緒進行并發,每個執行緒減55個庫存,我這里也不使用測驗工具jmeter了,就相當于單機測驗了,(如果是要進行分布式部署測驗,那么庫存值不能像我這樣直接在程式寫死 ,可以放redis或者資料庫,然后通過負載均衡、壓力測驗工具jmeter去完成,具體使用可以參考:windows下Nginx配置及負載均衡使用),我們主要目的是:為了驗證我們寫的分布式鎖,加深對分布式鎖的理解

TestLockService.java

package com.alian.zklock.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
public class TestLockService {

    @Autowired
    private ILockService lockService;

    AtomicInteger stock = new AtomicInteger(100);

    @PostConstruct
    public void testLock() {
        final CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    //使當前執行緒在鎖存器倒計數至零之前一直等待,除非執行緒被中斷或超出了指定的等待時間,如果當前計數為零,則此方法立刻回傳true值
                    countDownLatch.await();
                    //獲得鎖
                    boolean lock = lockService.lock("/root/alian", 10, TimeUnit.SECONDS);
                    if (lock) {
                        //業務處理
                        Thread.sleep(100);
                        //庫存減1
                        decrement();
                        //釋放鎖
                        lockService.release();
                        log.info("執行緒【{}】扣減完,剩余庫存:{}", Thread.currentThread().getName(), stock.get());
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {

                }
            }, "Thread" + i).start();
            //遞減鎖存器的計數,如果計數到達零,則釋放所有等待的執行緒,如果當前計數大于零,則將計數減少.
            countDownLatch.countDown();
        }
    }

    private void decrement() {
        for (int i = 0; i < 5; i++) {
            stock.decrementAndGet();
        }
    }

}

4.4、結果

運行結果圖:
在這里插入圖片描述
從我們的結果圖可以看出來(為了方便,節點前面的變化文章里就省略了,實際是存在的):

  • 同時三個執行緒(Thread0、Thread1、Thread2)創建了節點(180、179,181)去搶占資源
  • Thread1創建的179號節點是最小的,獲取到了鎖,這時候,Thread0監聽179節點,Thread2監聽180節點
  • Thread1扣減庫存5次,然后釋放鎖,也就是洗掉了節點179,觸發監聽
  • 因為Thread0監聽179節點,所以Thread0繼續執行搶占到了鎖,同樣扣減庫存后,洗掉180節點
  • 然后Thread2監聽的是180節點,同樣的Thread2搶占到了鎖,扣減庫存,洗掉181節點
  • 最后得到庫存85

超時的驗證則可以在業務執行的時候設定一個休眠時間,可重入鎖也是支持的,直接使用curator里面的,優秀的東西就直接拿來用了

4.5、關于CountDownLatch

也許有很多小伙伴,不知道CountDownLatch是怎么用的,我這里就簡單介紹下,主要有兩個方法:

  • public void countDown()

遞減鎖存器的計數,如果計數到達零,則釋放所有等待的執行緒,如果當前計數大于零,則將計數減少,

  • public boolean await(long timeout,TimeUnit unit) throws InterruptedException

??使當前執行緒在鎖存器倒計數至0之前一直等待,除非執行緒被中斷或超出了指定的等待時間,如果計數到達零,則回傳true;如果在計數到達零之前超過了等待時間,則回傳false,以下三種情況之一前,該執行緒將一直出于休眠狀態:

  • 如果計數到達零,則該方法回傳true值
  • 如果超出了指定的等待時間,則回傳值為false,如果該時間小于等于零,則該方法根本不會等待
  • 如果當前執行緒,在進入此方法時已經設定了該執行緒的中斷狀態;或者在等待時被中斷,則拋出InterruptedException,并且清除當前執行緒的已中斷狀態

??類似本文中的測驗方法,就相當于并發,當三個執行緒都創建完,都走到countDownLatch.await()這里就不執行了,直到執行countDownLatch.countDown()后面才會走,

    public void race() {
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    countDownLatch.await();
                    Thread.sleep(100);
                    log.info(Thread.currentThread().getName()+"開始跑步");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "Thread" + i).start();
        }
        countDownLatch.countDown();
        log.info("主執行緒執行完");
    }

結果:

2021-10-26 20:43:06 458 [main] INFO:主執行緒執行完
2021-10-26 20:43:06 561 [Thread2] INFO:Thread2開始跑步
2021-10-26 20:43:06 561 [Thread0] INFO:Thread0開始跑步
2021-10-26 20:43:06 561 [Thread1] INFO:Thread1開始跑步

??我們也可以反過來,使主執行緒阻塞,這個時候就是執行緒執行到countDownLatch.await()后,主執行緒后面的不執行,直到前面的子執行緒都執行完,主執行緒才往后執行,

    public void multitasking() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                log.info(Thread.currentThread().getName()+"執行完");
                countDownLatch.countDown();
            }, "Thread" + i).start();
        }
        countDownLatch.await();
        log.info("主執行緒執行完");
    }

結果:

2021-10-26 20:45:21 053 [Thread0] INFO:Thread0執行完
2021-10-26 20:45:21 053 [Thread1] INFO:Thread1執行完
2021-10-26 20:45:21 053 [Thread2] INFO:Thread2執行完
2021-10-26 20:45:21 053 [main] INFO:主執行緒執行完

結語

??也許本文的寫的分布式還有些許的瑕疵,但我們主要目的是:為了加深對zookeeper分布式鎖實作原理的理解,實際使用中我們還是使用curator是比較方便和穩定,具體可以參考我另外一篇文章:SpringBoot基于Zookeeper和Curator實作分布式鎖并分析其原理,

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

標籤:其他

上一篇:linux中下載安裝rabbitmq

下一篇:MapReduce學習筆記,理解學習Hadoop的MapReduce計算系統

標籤雲
其他(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