redigo 连接池代码分析

本文介绍了一个Redis连接池的实现细节,包括连接的创建、验证、关闭等操作,以及连接池如何管理连接的生命周期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结构体分析

type Pool struct {

    // 用来创建redis连接的方法
    Dial func() (Conn, error)

    // 如果设置了给func,那么每次p.Get()的时候都会调用改方法来验证连接的可用性
    TestOnBorrow func(c Conn, t time.Time) error

    // 定义连接池中最大连接数(超过这个数会关闭老的链接,总会保持这个数)
    MaxIdle int

    // 当前连接池中可用的链接数.
    MaxActive int

    // 定义链接的超时时间,每次p.Get()的时候会检测这个连接是否超时(超时会关闭,并释放可用连接数).
    IdleTimeout time.Duration

    // 当可用连接数为0是,那么当wait=true,那么当调用p.Get()时,会阻塞等待,否则,返回nil.
    Wait bool

    // 读写锁控制.
    mu     sync.Mutex
    // 用来条件控制,这里主要是当链接被关闭时,提醒在等待的进程可以使用了,或者可以自行创建了
    cond   *sync.Cond
    // 当前连接池是否已经关闭
    closed bool
    // 当前可用的链接数
    active int

    // 链接存储在一个栈中.
    idle list.List
}

连接池关闭方法

func (p *Pool) Close() error {
    p.mu.Lock()
    // 获取连接池所有链接栈
    idle := p.idle
    // 重新初始化
    p.idle.Init()
    // 标示已经关闭
    p.closed = true
    // 控制可用连接数
    p.active -= idle.Len()
    // 如果当前有进程正在等待获取的话,则通知可以获取或者自行创建
    if p.cond != nil {
        p.cond.Broadcast()
    }
    p.mu.Unlock()
    // 遍历栈,逐个关闭链接
    for e := idle.Front(); e != nil; e = e.Next() {
        e.Value.(idleConn).c.Close()
    }
    return nil
}

释放一个链接

func (p *Pool) release() {
    // 当链接超时,或者ping不通,或者创建失败,则立即使用链接表示
    p.active -= 1
    // 如果已经有进程在之前等待了,则通知其使用或者自行创建
    if p.cond != nil {
        p.cond.Signal()
    }
}

关闭连接

func (p *Pool) put(c Conn, forceClose bool) error {
    err := c.Err()
    p.mu.Lock()
    // 如果连接池没有关闭,并且不是强制关闭的
    if !p.closed && err == nil && !forceClose {
        // 把指定的链接放在空闲栈首位
        p.idle.PushFront(idleConn{t: nowFunc(), c: c})
        // 如果栈的长度大于指定长度,则吧最后一个(可能超时)剔除
        if p.idle.Len() > p.MaxIdle {
            c = p.idle.Remove(p.idle.Back()).(idleConn).c
        } else {
            c = nil
        }
    }

    if c == nil {
        //成功放回空闲连接通知其他阻塞的进程
        if p.cond != nil {
            p.cond.Signal()
        }
        p.mu.Unlock()
        return nil
    }
    // 减少active计数(感觉这里可以不用处理,上面如果是替换的话)
    p.release()
    p.mu.Unlock()
    // 关闭连接
    return c.Close()
}

获取链接

func (p *Pool) get() (Conn, error) {
    p.mu.Lock()
    // 处理旧的链接(检测是否超时)
    if timeout := p.IdleTimeout; timeout > 0 {
        for i, n := 0, p.idle.Len(); i < n; i++ {
            e := p.idle.Back()
            if e == nil {
                break
            }
            ic := e.Value.(idleConn)
            // 链接创建的时间加上超时时间是否小于当前时间
            if ic.t.Add(timeout).After(nowFunc()) {
                break
            }
            // 超时链接,从栈中删除
            p.idle.Remove(e)
            // 可用连接数减少,并通知其他等待的进程处理
            p.release()
            p.mu.Unlock()
            // 关闭当前连接
            ic.c.Close()
            p.mu.Lock()
        }
    }
    for {

        // 从连接栈列表中获取一个可用链接
        for i, n := 0, p.idle.Len(); i < n; i++ {
            // 从idle列表前面取连接,那么必然是刚刚使用过的连接
            e := p.idle.Front()
            if e == nil {
                break
            }
            // 类型断言
            ic := e.Value.(idleConn)
            // 从栈中删除
            p.idle.Remove(e)
            test := p.TestOnBorrow
            p.mu.Unlock()
            // 通过使用校验链接函数检测链接
            if test == nil || test(ic.c, ic.t) == nil {
                return ic.c, nil
            }
            // 检验出问题,关闭连接
            ic.c.Close()
            p.mu.Lock()
            // 降低可用连接数,并通知其他进程处理
            p.release()
        }

        // 检测连接池是否已经关闭.
        if p.closed {
            p.mu.Unlock()
            return nil, errors.New("redigo: get on closed pool")
        }

        // 如果可用连接数为0 或者小于最大可用可用连接数范围,那么创建
        if p.MaxActive == 0 || p.active < p.MaxActive {
            dial := p.Dial
            p.active += 1
            p.mu.Unlock()
            // 连接redis server
            c, err := dial()
            // 连接失败关闭之
            if err != nil {
                p.mu.Lock()
                p.release()
                p.mu.Unlock()
                c = nil
            }
            return c, err
        }
        // 如果没有配置等待,那么就返回nil
        if !p.Wait {
            p.mu.Unlock()
            return nil, ErrPoolExhausted
        }
        // 如果配置了等待,那么初始化,并且开始等待,直到有进程通知链接数够了或者可以创建了
        if p.cond == nil {
            p.cond = sync.NewCond(&p.mu)
        }
        p.cond.Wait()
    }
}
<think>好的,我现在需要帮用户了解BLE协议栈的解析方法和实现细节。首先,我需要回忆一下用户提供的引用内容,这些引用可能包含关键信息。用户提到了四个引用,分别涉及BLE协议栈的整体架构、链路层(LL)的作用、核心协议层中的物理层(PHY),以及Profile的结构。 用户的问题是关于解析方法和实现细节的,所以应该从协议栈的分层结构入手,逐层解析每个部分的功能和实现方式。根据引用[1],协议栈是实现BLE协议的代码,所以解析方法可能涉及各层的协作和数据流处理。引用[2]提到链路层是核心,负责数据包的发送和接收,处理射频通道选择、数据完整性等,这部分需要详细说明。物理层在引用[3]中也有提及,可能涉及调制方式和频段。应用层方面,引用[4]中的Profile结构需要解释,包括Service和Characteristic的组织。 接下来,我需要组织回答的结构。首先介绍整体架构,然后分层解析:物理层、链路层、主机控制接口(HCI)、主机层(包括L2CAP、ATT、GATT、GAP)、安全管理和应用层。每部分要结合引用的内容,比如链路层的功能引用了[2],Profile的结构引用了[4]。同时,需要确保使用正确的中文术语,并按照用户的要求使用LaTeX格式的数学表达式,但可能这里数学公式较少,主要注意代码块和分段。 然后,生成相关问题,可能需要考虑用户可能的后续问题,比如协议栈优化、数据包解析工具、各层交互机制等。需要确保问题与解析方法和实现细节相关,并参考之前的引用内容。 最后,检查是否符合用户的格式要求:行内公式用$...$,独立公式用$$...$$,中文回答,引用标识正确添加。用户提供的示例是关于快速排序的,但这里的主题是BLE协议栈,所以结构可能更偏向技术解析而非算法步骤。需要确保LaTeX语法正确,比如如果有数学表达式的话,但BLE协议栈的内容可能更多是文字描述和分层结构,较少涉及复杂公式,但如果有的话需要正确应用。 总结回答结构,确保每个部分引用正确的来源,例如链路层部分引用[2],Profile部分引用[4],整体架构引用[1]和[3]。同时,实现细节可能包括各层的职责、数据包处理流程、协议栈的配置等。需要详细但清晰地解释,帮助用户逐步理解协议栈的解析方法和实现。</think>### BLE协议栈解析方法及实现细节 #### 一、BLE协议栈分层架构 BLE协议栈采用分层设计,主要包括**控制器层**(Controller)和**主机层**(Host)[^1]。整体架构如下: $$ \text{应用层} \rightarrow \text{GATT/GAP} \rightarrow \text{ATT} \rightarrow \text{L2CAP} \rightarrow \text{HCI} \rightarrow \text{LL} \rightarrow \text{PHY} $$ #### 二、关键层级解析 1. **物理层(PHY)** - 工作频段:$2.4\ \text{GHz}$ ISM频段,划分40个物理通道(37个数据通道+3个广播通道)[^3] - 调制方式:GFSK(高斯频移键控),速率$1\ \text{Mbps}$ 2. **链路层(LL)** - 核心功能: - 信道选择(Frequency Hopping)与数据包时序控制 - 数据包结构: ```plaintext | Preamble | Access Address | PDU | CRC | ``` - 连接管理:支持多设备并行通信(如Nordic协议栈支持20个并发连接)[^2] - 实现关键:状态机设计(Advertising/Scanning/Initiating/Connected) 3. **主机控制接口(HCI)** - 作用:控制器与主机间的标准化通信接口 - 数据传输方式:UART/SPI/USB 4. **逻辑链路控制与适配协议(L2CAP)** - 功能:数据分片重组、协议多路复用 - 关键参数:MTU(最大传输单元)协商 5. **属性协议(ATT)与通用属性规范(GATT)** - 数据组织: $$ \text{Profile} \supset \text{Service} \supset \text{Characteristic} \supset \text{Descriptor} $$ 每个Characteristic包含UUID、权限、值等属性[^4] - 典型操作:`Read/Write/Notify/Indicate` #### 三、实现关键技术 1. **协议栈初始化流程** ```c ble_stack_init() → radio_init(PHY层配置) → ll_init(链路层参数) → hci_init(传输接口) → gatt_db_init(服务构建) ``` 2. **数据包解析示例** 广播包PDU结构解析: ```plaintext | Header (16-bit) | Payload (最多37字节) | ├─ PDU Type (4-bit) ├─ RFU (1-bit) ├─ ChSel (1-bit) └─ TxAdd/RxAdd (2-bit) ``` 3. **低功耗实现** - 事件驱动架构(Event-Driven Scheduling) - 占空比优化:连接间隔(Connection Interval)动态调整 #### 四、开发调试要点 1. **协议分析工具** - 使用Ellisys/Sniffer抓包分析空中接口数据 - 通过HCI Log追踪协议栈内部状态 2. **内存管理** - 预分配内存池(Memory Pool)避免动态分配 - 典型内存消耗: $$ \text{协议栈} \approx 50-100\ \text{KB ROM}, 10-20\ \text{KB RAM} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值