Jadual Kandungan
Teks
Kunci teragih
Jalankan ujian
Penapis yang diedarkan
Penghad arus teragih
运行测试
其他
Rumah pangkalan data Redis Cara menggunakan Redis dalam aplikasi yang diedarkan Golang

Cara menggunakan Redis dalam aplikasi yang diedarkan Golang

May 26, 2023 pm 10:07 PM
redis golang

    Teks

    Redis ialah pangkalan data dalam memori berprestasi tinggi yang sering digunakan dalam sistem teragih sebagai tambahan kepada cache teragih atau ringkas memori Pangkalan data juga mempunyai beberapa senario aplikasi khas Artikel ini menggabungkan Golang untuk menulis middleware yang sepadan.

    Kunci teragih

    Dalam sistem yang berdiri sendiri, kita boleh menggunakan sync.Mutex untuk melindungi sumber kritikal Terdapat juga keperluan sedemikian dalam sistem teragih , adalah perlu Tambah "kunci teragih" yang sepadan.

    Dalam Redis kita boleh menggunakan perintah setnx untuk mencapai ini

    • Jika kunci tidak wujud, anda boleh menetapkan nilai yang sepadan jika tetapan berjaya , kunci akan berjaya. Kunci tidak wujud

    • melepaskan kunci boleh dicapai melalui del.

    Logik utama adalah seperti berikut:

    type RedisLock struct {
    	client     *redis.Client
    	key        string
    	expiration time.Duration // 过期时间,防止宕机或者异常
    }
    func NewLock(client *redis.Client, key string, expiration time.Duration) *RedisLock {
    	return &RedisLock{
    		client:     client,
    		key:        key,
    		expiration: expiration,
    	}
    }
    // 加锁将成功会将调用者id保存到redis中
    func (l *RedisLock) Lock(id string) (bool, error) {
    	return l.client.SetNX(context.TODO(), l.key, id, l.expiration).Result()
    }
    const unLockScript = `
    if (redis.call("get", KEYS[1]) == KEYS[2]) then
    	redis.call("del", KEYS[1])
    	return true
    end
    return false
    `
    // 解锁通过lua脚本来保证原子性,只能解锁当前调用者加的锁
    func (l *RedisLock) UnLock(id string) error {
    	_, err := l.client.Eval(context.TODO(), unLockScript, []string{l.key, id}).Result()
    	if err != nil && err != redis.Nil {
    		return err
    	}
    	return nil
    }
    Salin selepas log masuk

    Untuk mengelakkan masa henti sistem atau kebuntuan yang disebabkan oleh permintaan yang tidak normal, tempoh tamat masa tambahan perlu ditambah, yang sepatutnya ditetapkan kepada Dua kali ganda anggaran masa larian maksimum.

    Skrip Lua digunakan untuk memastikan atomicity semasa membuka kunci Pemanggil hanya akan membuka kunci yang ditambahkan dengan sendirinya. Elakkan kekeliruan yang disebabkan oleh tamat masa Contohnya: Proses A memperoleh kunci pada masa t1, tetapi disebabkan pelaksanaan yang perlahan, kunci tamat masa pada masa t2. Proses B memperoleh kunci pada t3. proses akan dibatalkan kunci B.

    Jalankan ujian

    func main() {
        client := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "123456",
    		DB:       0, // use default DB
    	})
    	lock := NewLock(client, "counter", 30*time.Second)
        counter := 0
    	worker := func(i int) {
    		for {
    			id := fmt.Sprintf("worker%d", i)
    			ok, err := lock.Lock(id)
    			log.Printf("worker %d attempt to obtain lock, ok: %v, err: %v", i, ok, err)
    			if !ok {
    				time.Sleep(100 * time.Millisecond)
    				continue
    			}
    			defer lock.UnLock(id)
    			counter++
    			log.Printf("worker %d, add counter %d", i, counter)
    			break
    		}
    	}
    	wg := sync.WaitGroup{}
    	for i := 1; i <= 5; i++ {
    		wg.Add(1)
    		id := i
    		go func() {
    			defer wg.Done()
    			worker(id)
    		}()
    	}
    	wg.Wait()
    }
    Salin selepas log masuk

    Keputusan menunjukkan bahawa kesannya adalah serupa dengan sync.Mutex

    2022/07/22 09:58:09 pekerja 5 percubaan untuk mendapatkan kunci, ok: benar, err:
    2022/07/22 09:58:09 pekerja 5, tambah kaunter 1
    2022/07/22 09:58:09 pekerja 4 cuba untuk dapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:09 pekerja 1 percubaan untuk mendapatkan kunci, ok: palsu, err:
    2022/07/ 22 09:58:09 pekerja 2 percubaan untuk mendapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:09 pekerja 3 percubaan untuk mendapatkan kunci, ok: palsu, err: < ;nil>
    2022/07/22 09:58:10 pekerja 3 percubaan untuk mendapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:10 pekerja 1 percubaan untuk dapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:10 pekerja 2 cuba mendapatkan kunci, ok: palsu, err:
    2022/07/ 22 09:58:10 pekerja 4 percubaan untuk mendapatkan kunci, ok: benar, err:
    2022/07/22 09:58:10 pekerja 4, tambah kaunter 2
    2022/07/22 09:58:10 pekerja 1 percubaan untuk mendapatkan kunci, ok: benar, err:
    2022/07/22 09:58:10 pekerja 1, tambah kaunter 3
    2022/07/22 09 :58:10 pekerja 3 percubaan untuk mendapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:10 pekerja 2 cuba mendapatkan kunci, ok: palsu, err: 2022/07/22 09:58:10 pekerja 2 cuba mendapatkan kunci, ok: benar, err:
    2022/07/22 09:58:10 pekerja 2, tambah kaunter 4
    2022/07/22 09:58:10 pekerja 3 cuba mendapatkan kunci, ok: palsu, err:
    2022/07/22 09:58:10 pekerja 3 cuba mendapatkan kunci, ok: benar, err:
    2022/07/22 09:58:10 pekerja 3, tambah kaunter 5

    Perhatian khusus ialah dalam gugusan Redis yang diedarkan, Jika pengecualian berlaku (nod induk turun), ketersediaan kunci yang diedarkan mungkin dikurangkan, yang boleh dicapai melalui komponen konsistensi yang kuat seperti etcd dan ZooKeeper.

    Penapis yang diedarkan

    Andaikan kami ingin membangunkan perkhidmatan perangkak untuk merangkak berjuta-juta halaman web Bagaimana untuk menentukan sama ada halaman web tertentu telah dirangkak, selain menggunakan pangkalan data dan HashMap, kami boleh menggunakan penapis Bloom digunakan untuk melakukannya. Berbanding kaedah lain, penapis Bloom mengambil ruang yang sangat sedikit dan mempunyai masa pemasukan dan pertanyaan yang sangat pantas.

    Penapis Bloom digunakan untuk menentukan sama ada elemen berada dalam set Gunakan BitSet

    • untuk mencincang nilai beberapa kali apabila memasukkan data dan menetapkan kedudukan yang sepadan bagi BitSet to 1

    • Apabila membuat pertanyaan, berbilang operasi Hash juga dilakukan untuk membandingkan sama ada semua bit ialah 1. Jika ya, ia wujud.

    Penapis Bloom mempunyai kadar salah penilaian tertentu dan tidak sesuai untuk senario pertanyaan yang tepat. Selain itu, pemadaman elemen tidak disokong. Ia biasanya digunakan dalam senario seperti penyahduplikasian URL, penapisan spam dan pencegahan pecahan cache.

    Dalam Redis, kami boleh menggunakan pelaksanaan BitSet terbina dalam, dan juga menggunakan atomicity skrip lua untuk mengelakkan berbilang ketidakkonsistenan data pertanyaan.

    const (
    	// 插入数据,调用setbit设置对应位
    	setScript = `
    for _, offset in ipairs(ARGV) do
    	redis.call("setbit", KEYS[1], offset, 1)
    end
    `
    	// 查询数据,如果所有位都为1返回true
    	getScript = `
    for _, offset in ipairs(ARGV) do
    	if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
    		return false
    	end
    end
    return true
    `
    )
    type BloomFilter struct {
    	client *redis.Client
    	key    string // 存在redis中的key
    	bits   uint // BitSet的大小
    	maps   uint // Hash的次数
    }
    func NewBloomFilter(client *redis.Client, key string, bits, maps uint) *BloomFilter {
    	client.Del(context.TODO(), key)
    	if maps == 0 {
    		maps = 14
    	}
    	return &BloomFilter{
    		key:    key,
    		client: client,
    		bits:   bits,
    		maps:   maps,
    	}
    }
    // 进行多次Hash, 得到位置列表
    func (f *BloomFilter) getLocations(data []byte) []uint {
    	locations := make([]uint, f.maps)
    	for i := 0; i < int(f.maps); i++ {
    		val := murmur3.Sum64(append(data, byte(i)))
    		locations[i] = uint(val) % f.bits
    	}
    	return locations
    }
    func (f *BloomFilter) Add(data []byte) error {
    	args := getArgs(f.getLocations(data))
    	_, err := f.client.Eval(context.TODO(), setScript, []string{f.key}, args).Result()
    	if err != nil && err != redis.Nil {
    		return err
    	}
    	return nil
    }
    func (f *BloomFilter) Exists(data []byte) (bool, error) {
    	args := getArgs(f.getLocations(data))
    	resp, err := f.client.Eval(context.TODO(), getScript, []string{f.key}, args).Result()
    	if err != nil {
    		if err == redis.Nil {
    			return false, nil
    		}
    		return false, err
    	}
    	exists, ok := resp.(int64)
    	if !ok {
    		return false, nil
    	}
    	return exists == 1, nil
    }
    func getArgs(locations []uint) []string {
    	args := make([]string, 0)
    	for _, l := range locations {
    		args = append(args, strconv.FormatUint(uint64(l), 10))
    	}
    	return args
    }
    Salin selepas log masuk

    Jalankan ujian

    func main() {
    	bf := NewBloomFilter(client,"bf-test", 2^16, 14)
    	exists, err := bf.Exists([]byte("test1"))
    	log.Printf("exist %t, err %v", exists, err)
    	if err := bf.Add([]byte("test1")); err != nil {
    		log.Printf("add err: %v", err)
    	}
    	exists, err = bf.Exists([]byte("test1"))
    	log.Printf("exist %t, err %v", exists, err)
    	exists, err = bf.Exists([]byte("test2"))
    	log.Printf("exist %t, err %v", exists, err)
    // output
    // 2022/07/22 10:05:58 exist false, err <nil>
    // 2022/07/22 10:05:58 exist true, err <nil>
    // 2022/07/22 10:05:58 exist false, err <nil>
    }
    Salin selepas log masuk

    Penghad arus teragih

    Penghad arus berasaskan baldi token disediakan dalam pakej golang.org/x/time/rate, jika anda ingin melaksanakan persekitaran teragih Pengehadan semasa boleh dilaksanakan berdasarkan skrip Redis Lua.

    Prinsip utama baldi token adalah seperti berikut:

    • Anggapkan bahawa kapasiti baldi token pecah, dan token diletakkan di dalamnya pada kadar qps setiap saat

    • Pada mulanya, token diisi Jika token melimpah, ia akan dibuang terus apabila meminta token, jika terdapat cukup token dalam baldi, ia akan dibenarkan , jika tidak, ia akan ditolak

    • Apabila pecah==qps, had aliran benar-benar mengikut qps apabila pecah>qps, letusan trafik tertentu boleh dibenarkan

    这里主要参考了官方rate包的实现,将核心逻辑改为Lua实现。

    --- 相关Key
    --- limit rate key值,对应value为当前令牌数
    local limit_key = KEYS[1]
    --- 输入参数
    --[[
    qps: 每秒请求数;
    burst: 令牌桶容量;
    now: 当前Timestamp;
    cost: 请求令牌数;
    max_wait: 最大等待时间
    --]]
    local qps = tonumber(ARGV[1])
    local burst = tonumber(ARGV[2])
    local now = ARGV[3]
    local cost = tonumber(ARGV[4])
    local max_wait = tonumber(ARGV[5])
    --- 获取redis中的令牌数
    local tokens = redis.call("hget", limit_key, "token")
    if not tokens then
    	tokens = burst
    end
    --- 上次修改时间
    local last_time = redis.call("hget", limit_key, "last_time")
    if not last_time then
    	last_time = 0
    end
    --- 最新等待时间
    local last_event = redis.call("hget", limit_key, "last_event")
    if not last_event then
    	last_event = 0
    end
    --- 通过当前时间与上次修改时间的差值,qps计算出当前时间得令牌数
    local delta = math.max(0, now-last_time)
    local new_tokens = math.min(burst, delta * qps + tokens)
    new_tokens = new_tokens - cost --- 最新令牌数,减少请求令牌
    --- 如果最新令牌数小于0,计算需要等待的时间
    local wait_period = 0
    if new_tokens < 0 and qps > 0 then
    	wait_period = wait_period - new_tokens / qps
    end
    wait_period = math.ceil(wait_period)
    local time_act = now + wait_period --- 满足等待间隔的时间戳
    --- 允许请求有两种情况
    --- 当请求令牌数小于burst, 等待时间不超过最大等待时间,可以通过补充令牌满足请求
    --- qps为0时,只要最新令牌数不小于0即可
    local ok = (cost <= burst and wait_period <= max_wait and qps > 0) or (qps == 0 and new_tokens >= 0)
    --- 设置对应值
    if ok then
    	redis.call("set", limit_key, new_tokens)
    	redis.call("set", last_time_key, now)
    	redis.call("set", last_event_key, time_act)
    end
    --- 返回列表,{是否允许, 等待时间}
    return {ok, wait_period}
    Salin selepas log masuk

    在Golang中的相关接口Allow、AllowN、Wait等都是通过调用reserveN实现

    // 调用lua脚本
    func (lim *RedisLimiter) reserveN(now time.Time, n int, maxFutureReserveSecond int) (*Reservation, error) {
    	// ...
    	res, err := lim.rdb.Eval(context.TODO(), reserveNScript, []string{lim.limitKey}, lim.qps, lim.burst, now.Unix(), n, maxFutureReserveSecond).Result()
    	if err != nil && err != redis.Nil {
    		return nil, err
    	}
    	//...
    	return &Reservation{
    		ok:        allow == 1,
    		lim:       lim,
    		tokens:    n,
    		timeToAct: now.Add(time.Duration(wait) * time.Second),
    	}, nil
    }
    Salin selepas log masuk

    运行测试

    func main() {
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "123456",
    		DB:       0, // use default DB
    	})
    	r, err := NewRedisLimiter(rdb, 1, 2, "testrate")
    	if err != nil {
    		log.Fatal(err)
    	}
    	r.Reset()
    	for i := 0; i < 5; i++ {
    		err := r.Wait(context.TODO())
    		log.Printf("worker %d allowed: %v", i, err)
    	}
    }
    // output
    // 2022/07/22 12:50:31 worker 0 allowed: <nil>
    // 2022/07/22 12:50:31 worker 1 allowed: <nil>
    // 2022/07/22 12:50:32 worker 2 allowed: <nil>
    // 2022/07/22 12:50:33 worker 3 allowed: <nil>
    // 2022/07/22 12:50:34 worker 4 allowed: <nil>
    Salin selepas log masuk

    前两个请求在burst内,直接可以获得,后面的请求按照qps的速率生成。

    其他

    Redis还可用于全局计数、去重以及发布订阅等不同情境。参考Redis官方提供的模块,可以通过加载这些模块实现过滤、限流等特性。

    Atas ialah kandungan terperinci Cara menggunakan Redis dalam aplikasi yang diedarkan Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan Laman Web ini
    Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

    Alat AI Hot

    Undresser.AI Undress

    Undresser.AI Undress

    Apl berkuasa AI untuk mencipta foto bogel yang realistik

    AI Clothes Remover

    AI Clothes Remover

    Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

    Undress AI Tool

    Undress AI Tool

    Gambar buka pakaian secara percuma

    Clothoff.io

    Clothoff.io

    Penyingkiran pakaian AI

    Video Face Swap

    Video Face Swap

    Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

    Alat panas

    Notepad++7.3.1

    Notepad++7.3.1

    Editor kod yang mudah digunakan dan percuma

    SublimeText3 versi Cina

    SublimeText3 versi Cina

    Versi Cina, sangat mudah digunakan

    Hantar Studio 13.0.1

    Hantar Studio 13.0.1

    Persekitaran pembangunan bersepadu PHP yang berkuasa

    Dreamweaver CS6

    Dreamweaver CS6

    Alat pembangunan web visual

    SublimeText3 versi Mac

    SublimeText3 versi Mac

    Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

    Cara Membina Mod Kluster Redis Cara Membina Mod Kluster Redis Apr 10, 2025 pm 10:15 PM

    Mod Redis cluster menyebarkan contoh Redis ke pelbagai pelayan melalui sharding, meningkatkan skalabilitas dan ketersediaan. Langkah -langkah pembinaan adalah seperti berikut: Buat contoh Redis ganjil dengan pelabuhan yang berbeza; Buat 3 contoh sentinel, memantau contoh redis dan failover; Konfigurasi fail konfigurasi sentinel, tambahkan pemantauan maklumat contoh dan tetapan failover; Konfigurasi fail konfigurasi contoh Redis, aktifkan mod kluster dan tentukan laluan fail maklumat kluster; Buat fail nodes.conf, yang mengandungi maklumat setiap contoh Redis; Mulakan kluster, laksanakan perintah Buat untuk membuat kluster dan tentukan bilangan replika; Log masuk ke kluster untuk melaksanakan perintah maklumat kluster untuk mengesahkan status kluster; buat

    Cara membersihkan data redis Cara membersihkan data redis Apr 10, 2025 pm 10:06 PM

    Cara Mengosongkan Data Redis: Gunakan perintah Flushall untuk membersihkan semua nilai utama. Gunakan perintah flushdb untuk membersihkan nilai utama pangkalan data yang dipilih sekarang. Gunakan Pilih untuk menukar pangkalan data, dan kemudian gunakan FlushDB untuk membersihkan pelbagai pangkalan data. Gunakan perintah DEL untuk memadam kunci tertentu. Gunakan alat REDIS-CLI untuk membersihkan data.

    Cara Membaca Gilir Redis Cara Membaca Gilir Redis Apr 10, 2025 pm 10:12 PM

    Untuk membaca giliran dari Redis, anda perlu mendapatkan nama giliran, membaca unsur -unsur menggunakan arahan LPOP, dan memproses barisan kosong. Langkah-langkah khusus adalah seperti berikut: Dapatkan nama giliran: Namakannya dengan awalan "giliran:" seperti "giliran: my-queue". Gunakan arahan LPOP: Keluarkan elemen dari kepala barisan dan kembalikan nilainya, seperti LPOP Queue: My-Queue. Memproses Baris kosong: Jika barisan kosong, LPOP mengembalikan nihil, dan anda boleh menyemak sama ada barisan wujud sebelum membaca elemen.

    Cara menggunakan baris arahan redis Cara menggunakan baris arahan redis Apr 10, 2025 pm 10:18 PM

    Gunakan alat baris perintah redis (redis-cli) untuk mengurus dan mengendalikan redis melalui langkah-langkah berikut: Sambungkan ke pelayan, tentukan alamat dan port. Hantar arahan ke pelayan menggunakan nama arahan dan parameter. Gunakan arahan bantuan untuk melihat maklumat bantuan untuk arahan tertentu. Gunakan perintah berhenti untuk keluar dari alat baris arahan.

    Pengoptimuman Prestasi PostgreSQL di bawah Debian Pengoptimuman Prestasi PostgreSQL di bawah Debian Apr 12, 2025 pm 08:18 PM

    Untuk meningkatkan prestasi pangkalan data PostgreSQL dalam sistem Debian, adalah perlu untuk secara komprehensif mempertimbangkan perkakasan, konfigurasi, pengindeksan, pertanyaan dan aspek lain. Strategi berikut dapat mengoptimumkan prestasi pangkalan data dengan berkesan: 1. Pengembangan Memori Pengoptimuman Sumber Perkakasan: Memori yang mencukupi adalah penting untuk data cache dan indeks. Penyimpanan berkelajuan tinggi: Menggunakan pemacu SSD SSD dapat meningkatkan prestasi I/O dengan ketara. Pemproses Multi-Core: Buat penggunaan penuh pemproses pelbagai teras untuk melaksanakan pemprosesan pertanyaan selari. 2. Parameter pangkalan data penalaan shared_buffers: Menurut tetapan saiz memori sistem, disarankan untuk menetapkannya kepada 25% -40% memori sistem. Work_mem: Mengawal ingatan pengendalian dan operasi hashing, biasanya ditetapkan kepada 64MB hingga 256m

    C dan Golang: Apabila prestasi sangat penting C dan Golang: Apabila prestasi sangat penting Apr 13, 2025 am 12:11 AM

    C lebih sesuai untuk senario di mana kawalan langsung sumber perkakasan dan pengoptimuman prestasi tinggi diperlukan, sementara Golang lebih sesuai untuk senario di mana pembangunan pesat dan pemprosesan konkurensi tinggi diperlukan. Kelebihan 1.C terletak pada ciri-ciri perkakasan dan keupayaan pengoptimuman yang tinggi, yang sesuai untuk keperluan berprestasi tinggi seperti pembangunan permainan. 2. Kelebihan Golang terletak pada sintaks ringkas dan sokongan konvensional semulajadi, yang sesuai untuk pembangunan perkhidmatan konvensional yang tinggi.

    Cara Menetapkan Dasar Tamat Redis Cara Menetapkan Dasar Tamat Redis Apr 10, 2025 pm 10:03 PM

    Terdapat dua jenis strategi tamat tempoh data REDIS: Penghapusan berkala: Imbasan berkala untuk memadamkan kunci yang telah tamat tempoh, yang boleh ditetapkan melalui parameter-cap-cap-rempah yang telah tamat tempoh dan parameter kelewatan-cap-remove-time-time. Penghapusan Lazy: Periksa kekunci yang telah tamat tempoh hanya apabila kunci dibaca atau ditulis. Mereka boleh ditetapkan melalui parameter lazon-lazy-expire-expire-expire, lazy-lazy-user-del parameter.

    Impak Golang: Kelajuan, Kecekapan, dan Kesederhanaan Impak Golang: Kelajuan, Kecekapan, dan Kesederhanaan Apr 14, 2025 am 12:11 AM

    Goimpactsdevelopmentpositivielythroughspeed, efficiency, andsimplicity.1) Speed: goCompilesquicklyandrunsefficiently, idealforlargeproject.2) Kecekapan: ITSComprehensivestandardlibraryraryrarexternaldependencies, enhingdevelyficiency.

    See all articles