欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang?Redis連接池實(shí)現(xiàn)原理及示例探究

 更新時(shí)間:2024年01月26日 08:42:24   作者:紹納?nullbody筆記  
這篇文章主要為大家介紹了Golang?Redis連接池實(shí)現(xiàn)示例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

用11篇文章實(shí)現(xiàn)一個(gè)可用的Redis服務(wù),姑且叫EasyRedis吧,希望通過文章將Redis掰開撕碎了呈現(xiàn)給大家,而不是僅僅停留在八股文的層面,并且有非常爽的感覺,歡迎持續(xù)關(guān)注學(xué)習(xí)。

項(xiàng)目代碼地址: https://github.com/gofish2020/easyredis 

  • [x] easyredis之TCP服務(wù)
  • [x] easyredis之網(wǎng)絡(luò)請(qǐng)求序列化協(xié)議(RESP)
  • [x] easyredis之內(nèi)存數(shù)據(jù)庫
  • [x] easyredis之過期時(shí)間 (時(shí)間輪實(shí)現(xiàn))
  • [x] easyredis之持久化 (AOF實(shí)現(xiàn))
  • [x] easyredis之發(fā)布訂閱功能
  • [x] easyredis之有序集合(跳表實(shí)現(xiàn))
  • [x] easyredis之 pipeline 客戶端實(shí)現(xiàn)
  • [x] easyredis之事務(wù)(原子性/回滾)
  • [x] easyredis之連接池
  • [ ] easyredis之分布式集群存儲(chǔ)

Redis之連接池

通過本篇可以學(xué)到什么?

通道的應(yīng)用

連接池的封裝

從本篇開始,實(shí)現(xiàn)分布式相關(guān)的代碼。既然是分布式,那么redis key就會(huì)分布(分散)在不同的集群節(jié)點(diǎn)上。

當(dāng)客戶端發(fā)送set key value命令給Redis0服務(wù),通過hash計(jì)算如果該key應(yīng)該保存在Redis2服務(wù),那么Redis0就要連接Redis2服務(wù),并將命令轉(zhuǎn)發(fā)給Redis2進(jìn)行處理。

在命令的轉(zhuǎn)發(fā)的過程中,需要頻繁的連接分布式節(jié)點(diǎn),所以我們需要先實(shí)現(xiàn)連接池的基本功能,復(fù)用連接。

第八篇pipeline客戶端我們已經(jīng)實(shí)現(xiàn)了客戶端連接,本篇需要實(shí)現(xiàn)一個(gè)池子的功能將已經(jīng)使用完的連接緩存起來,等到需要使用的時(shí)候,再取出來繼續(xù)使用。

代碼路徑tool/pool/pool.go,代碼量160行

池子結(jié)構(gòu)體定義

  • 既然是池子,那定義的數(shù)據(jù)結(jié)構(gòu)里面肯定要有個(gè)緩沖的變量,這里就是idles chan any

  • 一開始池子中肯定是沒有對(duì)象的,所以需要有個(gè)能夠創(chuàng)建對(duì)象的函數(shù) newObject

  • 配套有個(gè)釋放對(duì)象的函數(shù)freeObject

  • 池子中的對(duì)象不可能讓他無限的增多,當(dāng)達(dá)到activeCount個(gè)對(duì)象的時(shí)候,就不再繼續(xù)用newObject生成新對(duì)象,需要等之前的對(duì)象回收以后,才能獲取到對(duì)象(這里不理解往下繼續(xù)看)

type Pool struct {
	Config
	// 創(chuàng)建對(duì)象
	newObject func() (any, error)
	// 釋放對(duì)象
	freeObject func(x any)
	// 空閑對(duì)象池
	idles chan any
	mu          sync.Mutex
	activeCount int// 已經(jīng)創(chuàng)建的對(duì)象個(gè)數(shù)
	waiting     []chan any // 阻塞等待
	closed bool// 是否已關(guān)閉
}
func NewPool(new func() (any, error), free func(x any), conf Config) *Pool {
	ifnew == nil {
		logger.Error("NewPool argument new func is nil")
		returnnil
	}
	if free == nil {
		free = func(x any) {}
	}
	p := Pool{
		Config:      conf,
		newObject:   new,
		freeObject:  free,
		activeCount: 0,
		closed:      false,
	}
	p.idles = make(chan any, p.MaxIdles)
	return &p
}

從池子中獲取對(duì)象

  • p.mu.Lock()加鎖(race condition)
  • 從空閑緩沖idles中獲取一個(gè)之前緩沖的對(duì)象
  • 如果沒有獲取到就調(diào)用p.getOne()新創(chuàng)建一個(gè)
  • func (p *Pool) getOne() (any, error)函數(shù)中,會(huì)判斷當(dāng)前池子中是否(歷史上)已經(jīng)創(chuàng)建了足夠多的對(duì)象p.activeCount >= p.Config.MaxActive ,那就不創(chuàng)建新對(duì)象,阻塞等待回收;否則調(diào)用newObject函數(shù)創(chuàng)建新對(duì)象
func (p *Pool) Get() (any, error) {
	p.mu.Lock()
	if p.closed {
		p.mu.Unlock()
		returnnil, ErrClosed
	}
	select {
	case x := <-p.idles: // 從空閑中獲取
		p.mu.Unlock() // 解鎖
		return x, nil
	default:
		return p.getOne() // 獲取一個(gè)新的
	}
}
func (p *Pool) getOne() (any, error) {
	// 說明已經(jīng)創(chuàng)建了太多對(duì)象
	if p.activeCount >= p.Config.MaxActive {
		wait := make(chan any, 1)
		p.waiting = append(p.waiting, wait)
		p.mu.Unlock()
		// 阻塞等待
		x, ok := <-wait
		if !ok {
			returnnil, ErrClosed
		}
		return x, nil
	}
	p.activeCount++
	p.mu.Unlock()
	// 創(chuàng)建新對(duì)象
	x, err := p.newObject()
	if err != nil {
		p.mu.Lock()
		p.activeCount--
		p.mu.Unlock()
		returnnil, err
	}
	return x, nil
}

池子對(duì)象回收

回收的過程就是對(duì)象緩存的過程,當(dāng)然也要有個(gè)“度”

先加鎖

回收前先判斷是否有阻塞等待回收len(p.waiting) > 0,這里的邏輯和上面的等待阻塞邏輯對(duì)應(yīng)起來了

如果沒有阻塞等待的,那就直接將對(duì)象保存到緩沖中idles

  • 這里還有一個(gè)邏輯,緩沖有個(gè)大小限制(不可能無限的緩沖,多余不使用的對(duì)象,我們將它釋放了,占用內(nèi)存也沒啥意義)
func (p *Pool) Put(x any) {
	p.mu.Lock()
	if p.closed {
		p.mu.Unlock()
		p.freeObject(x) // 直接釋放
		return
	}

	//1.先判斷等待中
	iflen(p.waiting) > 0 {
		// 彈出一個(gè)(從頭部)
		wait := p.waiting[0]
		temp := make([]chan any, len(p.waiting)-1)
		copy(temp, p.waiting[1:])
		p.waiting = temp
		wait <- x // 取消阻塞
		p.mu.Unlock()
		return

	}
	// 2.直接放回空閑緩沖
	select {
	case p.idles <- x:
		p.mu.Unlock()
	default: // 說明空閑已滿
		p.activeCount-- // 對(duì)象個(gè)數(shù)-1
		p.mu.Unlock()
		p.freeObject(x) // 釋放
	}

}

再次封裝(socket連接池)

上面的代碼已經(jīng)完全實(shí)現(xiàn)了一個(gè)池子的功能;但是我們?cè)趯?shí)際使用的時(shí)候,每個(gè)ip地址對(duì)應(yīng)一個(gè)連接池,所以這里又增加了一個(gè)結(jié)構(gòu)體RedisConnPool,結(jié)合上面的池子功能,再配合之前的pipleline客戶端的功能,實(shí)現(xiàn)socket連接池。

代碼路徑:cluster/conn_pool.go代碼邏輯:

  • 用一個(gè)字典key表示ip地址,value表示上面實(shí)現(xiàn)的池對(duì)象

  • GetConn獲取一個(gè)ip地址對(duì)應(yīng)的連接

  • ReturnConn歸還連接到連接池中

type RedisConnPool struct {
	connDict *dict.ConcurrentDict // addr -> *pool.Pool
}
func NewRedisConnPool() *RedisConnPool {
	return &RedisConnPool{
		connDict: dict.NewConcurrentDict(16),
	}
}
func (r *RedisConnPool) GetConn(addr string) (*client.RedisClent, error) {
	var connectionPool *pool.Pool // 對(duì)象池
	// 通過不同的地址addr,獲取不同的對(duì)象池
	raw, ok := r.connDict.Get(addr)
	if ok {
		connectionPool = raw.(*pool.Pool)
	} else {
		// 創(chuàng)建對(duì)象函數(shù)
		newClient := func() (any, error) {
			// redis的客戶端連接
			cli, err := client.NewRedisClient(addr)
			if err != nil {
				returnnil, err
			}
			// 啟動(dòng)
			cli.Start()
			if conf.GlobalConfig.RequirePass != "" { // 說明服務(wù)需要密碼
				reply, err := cli.Send(aof.Auth([]byte(conf.GlobalConfig.RequirePass)))
				if err != nil {
					returnnil, err
				}
				if !protocol.IsOKReply(reply) {
					returnnil, errors.New("auth failed:" + string(reply.ToBytes()))
				}
				return cli, nil
			}
			return cli, nil
		}
		// 釋放對(duì)象函數(shù)
		freeClient := func(x any) {
			cli, ok := x.(*client.RedisClent)
			if ok {
				cli.Stop() // 釋放
			}
		}
		// 針對(duì)addr地址,創(chuàng)建一個(gè)新的對(duì)象池
		connectionPool = pool.NewPool(newClient, freeClient, pool.Config{
			MaxIdles:  1,
			MaxActive: 20,
		})
		// addr -> *pool.Pool
		r.connDict.Put(addr, connectionPool)
	}
	// 從對(duì)象池中獲取一個(gè)對(duì)象
	raw, err := connectionPool.Get()
	if err != nil {
		returnnil, err
	}
	conn, ok := raw.(*client.RedisClent)
	if !ok {
		returnnil, errors.New("connection pool make wrong type")
	}
	return conn, nil
}
func (r *RedisConnPool) ReturnConn(peer string, cli *client.RedisClent) error {
	raw, ok := r.connDict.Get(peer)
	if !ok {
		return errors.New("connection pool not found")
	}
	raw.(*pool.Pool).Put(cli)
	returnnil
}

以上就是Golang實(shí)現(xiàn)Redis之連接池的詳細(xì)內(nèi)容,更多關(guān)于Golang Redis連接池的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼

    本文主要介紹了Go實(shí)現(xiàn)替換(覆蓋)文件某一行內(nèi)容的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • golang通過cgo調(diào)用C++庫源碼示例

    golang通過cgo調(diào)用C++庫源碼示例

    這篇文章主要給大家介紹了關(guān)于golang通過cgo調(diào)用C++庫的相關(guān)資料,CGO是GO語言里面的一個(gè)特性,CGO屬于GOLANG的高級(jí)用法,主要是通過使用GOLANG調(diào)用CLANG實(shí)現(xiàn)的程序庫,需要的朋友可以參考下
    2024-02-02
  • Go語言for range(按照鍵值循環(huán))遍歷操作

    Go語言for range(按照鍵值循環(huán))遍歷操作

    這篇文章主要介紹了Go語言for range(按照鍵值循環(huán))遍歷操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go中的條件語句Switch示例詳解

    Go中的條件語句Switch示例詳解

    Go的switch的基本功能和C、Java類似,switch 語句用于基于不同條件執(zhí)行不同動(dòng)作,每一個(gè) case 分支都是唯一的,從上至下逐一測試,直到匹配為止,對(duì)Go條件語句Switch相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-08-08
  • 深入理解 Go 語言中的 Context

    深入理解 Go 語言中的 Context

    這篇文章主要介紹了 理解 Go 語言中的 Context,需要的朋友可以參考下
    2020-06-06
  • 解讀golang plugin熱更新嘗試

    解讀golang plugin熱更新嘗試

    這篇文章主要介紹了解讀golang plugin熱更新嘗試,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-04-04
  • 詳解Go開發(fā)Struct轉(zhuǎn)換成map兩種方式比較

    詳解Go開發(fā)Struct轉(zhuǎn)換成map兩種方式比較

    本篇文章主要介紹了詳解Go開發(fā)Struct轉(zhuǎn)換成map兩種方式比較,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過程詳解

    Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過程詳解

    這篇文章主要介紹了Go結(jié)合反射將結(jié)構(gòu)體轉(zhuǎn)換成Excel的過程詳解,大概思路是在Go的結(jié)構(gòu)體中每個(gè)屬性打上一個(gè)excel標(biāo)簽,利用反射獲取標(biāo)簽中的內(nèi)容,作為表格的Header,需要的朋友可以參考下
    2022-06-06
  • go?pprof?的使用操作代碼

    go?pprof?的使用操作代碼

    pprof 是 go 中進(jìn)行性能分析的工具,可以提供可視化數(shù)據(jù)查看,這篇文章主要介紹了go?pprof?的使用操作,需要的朋友可以參考下
    2022-09-09
  • 一文教你如何快速學(xué)會(huì)Go的struct數(shù)據(jù)類型

    一文教你如何快速學(xué)會(huì)Go的struct數(shù)據(jù)類型

    結(jié)構(gòu)是表示字段集合的用戶定義類型。它可以用于將數(shù)據(jù)分組為單個(gè)單元而不是將每個(gè)數(shù)據(jù)作為單獨(dú)的值的地方。本文就來和大家聊聊Go中struct數(shù)據(jù)類型的使用,需要的可以參考一下
    2023-03-03

最新評(píng)論