Cara menggunakan Redis dalam aplikasi yang diedarkan 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 }
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() }
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 }
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> }
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}
在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 }
运行测试
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>
前两个请求在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!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

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

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



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 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.

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.

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.

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 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.

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.

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