zoukankan      html  css  js  c++  java
  • redigo 学习笔记

    redigo

    接口 Conn

    type Conn interface {
    	// Close closes the connection.
    	Close() error
    
    	// Err returns a non-nil value when the connection is not usable.
    	Err() error
    
    	// Do sends a command to the server and returns the received reply.
    	Do(commandName string, args ...interface{}) (reply interface{}, err error)
    
    	// Send writes the command to the client's output buffer.
    	Send(commandName string, args ...interface{}) error
    
    	// Flush flushes the output buffer to the Redis server.
    	Flush() error
    
    	// Receive receives a single reply from the Redis server
    	Receive() (reply interface{}, err error)
    }
    

    获取 Conn

    常用 Dial 方法来获取一个 redis 链接

    c, err := redis.Dial("tcp", "localhost:6379",
        redis.DialUsername("username"),
        redis.DialPassword("password"),
    )
    if err != nil {
        // handle error
    }
    defer c.Close()
    
    
    // Dial connects to the Redis server at the given network and
    // address using the specified options.
    func Dial(network, address string, options ...DialOption) (Conn, error) {
    	return DialContext(context.Background(), network, address, options...)
    }
    
    // DialContext connects to the Redis server at the given network and
    // address using the specified options and context.
    func DialContext(ctx context.Context, network, address string, options ...DialOption) (Conn, error) {
    	do := dialOptions{
    		dialer: &net.Dialer{
    			KeepAlive: time.Minute * 5,
    		}, 
    	}
    	for _, option := range options {
    		option.f(&do) // 简化 赋值 
    	}
    	if do.dialContext == nil {
    		do.dialContext = do.dialer.DialContext
    	}
    
    	netConn, err := do.dialContext(ctx, network, address) //使用 net.Dialer  dialContext 连接服务器
    	if err != nil {
    		return nil, err
    	}
    
    	if do.useTLS {
    		var tlsConfig *tls.Config
    		if do.tlsConfig == nil {
    			tlsConfig = &tls.Config{InsecureSkipVerify: do.skipVerify}
    		} else {
    			tlsConfig = cloneTLSConfig(do.tlsConfig)
    		}
    		if tlsConfig.ServerName == "" {
    			host, _, err := net.SplitHostPort(address)
    			if err != nil {
    				netConn.Close()
    				return nil, err
    			}
    			tlsConfig.ServerName = host
    		}
    
    		tlsConn := tls.Client(netConn, tlsConfig)
    		if err := tlsConn.Handshake(); err != nil {
    			netConn.Close()
    			return nil, err
    		}
    		netConn = tlsConn
    	}
    
    	c := &conn{
    		conn:         netConn, // net.Conn 构造 Conn返回
    		bw:           bufio.NewWriter(netConn),
    		br:           bufio.NewReader(netConn),
    		readTimeout:  do.readTimeout,
    		writeTimeout: do.writeTimeout,
    	}
    
    	if do.password != "" {
    		authArgs := make([]interface{}, 0, 2)
    		if do.username != "" {
    			authArgs = append(authArgs, do.username)
    		}
    		authArgs = append(authArgs, do.password)
    		if _, err := c.Do("AUTH", authArgs...); err != nil { // auth
    			netConn.Close()
    			return nil, err
    		}
    	}
    
    	if do.clientName != "" {
    		if _, err := c.Do("CLIENT", "SETNAME", do.clientName); err != nil {
    			netConn.Close()
    			return nil, err
    		}
    	}
    
    	if do.db != 0 {
    		if _, err := c.Do("SELECT", do.db); err != nil { // db
    			netConn.Close()
    			return nil, err
    		}
    	}
    
    	return c, nil
    }
    
    

    执行 Do

    
    
    func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
    	return c.DoWithTimeout(c.readTimeout, cmd, args...)
    }
    
    func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {
    	c.mu.Lock()
    	pending := c.pending
    	c.pending = 0
    	c.mu.Unlock()
    
    	if cmd == "" && pending == 0 {
    		return nil, nil
    	}
    
    	if c.writeTimeout != 0 {
    		c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout)) //设置 写超时时间
    	}
    
    	if cmd != "" {
    		if err := c.writeCommand(cmd, args); err != nil { //写命令
    			return nil, c.fatal(err)
    		}
    	}
    
    	if err := c.bw.Flush(); err != nil {
    		return nil, c.fatal(err)
    	}
    
    	var deadline time.Time
    	if readTimeout != 0 {
    		deadline = time.Now().Add(readTimeout)
    	}
    	c.conn.SetReadDeadline(deadline) // 设置 读超时时间
    
    	if cmd == "" {
    		reply := make([]interface{}, pending)
    		for i := range reply {
    			r, e := c.readReply()
    			if e != nil {
    				return nil, c.fatal(e)
    			}
    			reply[i] = r
    		}
    		return reply, nil
    	}
    
    	var err error
    	var reply interface{}
    	for i := 0; i <= pending; i++ {
    		var e error
    		if reply, e = c.readReply(); e != nil { //readReply 读取结果
    			return nil, c.fatal(e)
    		}
    		if e, ok := reply.(Error); ok && err == nil {
    			err = e
    		}
    	}
    	return reply, err
    }
    

    发送命令

    redis 基本协议

    *<参数数量> CR LF
    $<参数 1 的字节数量> CR LF
    <参数 1 的数据> CR LF
    ...
    $<参数 N 的字节数量> CR LF
    <参数 N 的数据> CR LF
    
    示例:
    *3
    $3
    SET
    $5
    mykey
    $7
    myvalue
    
    func (c *conn) writeCommand(cmd string, args []interface{}) error {
    	c.writeLen('*', 1+len(args))  // *<参数数量> CR LF
    	if err := c.writeString(cmd); err != nil { // $<参数 1 的字节数量> CR LF <参数 1 的数据> CR LF
    		return err 
    	}
    	for _, arg := range args {
    		if err := c.writeArg(arg, true); err != nil { // $<参数 N 的字节数量> CR LF <参数 N 的数据> CR LF
    			return err
    		}
    	}
    	return nil
    }
    
    

    读取结果

    func (c *conn) readReply() (interface{}, error) {
    	line, err := c.readLine()
    	if err != nil {
    		return nil, err
    	}
    	if len(line) == 0 {
    		return nil, protocolError("short response line")
    	}
    	switch line[0] {
    	case '+':
    		switch string(line[1:]) {
    		case "OK":
    			// Avoid allocation for frequent "+OK" response.
    			return okReply, nil
    		case "PONG":
    			// Avoid allocation in PING command benchmarks :)
    			return pongReply, nil
    		default:
    			return string(line[1:]), nil
    		}
    	case '-':
    		return Error(string(line[1:])), nil
    	case ':':
    		return parseInt(line[1:])
    	case '$':
    		n, err := parseLen(line[1:])
    		if n < 0 || err != nil {
    			return nil, err
    		}
    		p := make([]byte, n)
    		_, err = io.ReadFull(c.br, p)
    		if err != nil {
    			return nil, err
    		}
    		if line, err := c.readLine(); err != nil {
    			return nil, err
    		} else if len(line) != 0 {
    			return nil, protocolError("bad bulk string format")
    		}
    		return p, nil
    	case '*':
    		n, err := parseLen(line[1:])
    		if n < 0 || err != nil {
    			return nil, err
    		}
    		r := make([]interface{}, n)
    		for i := range r {
    			r[i], err = c.readReply()
    			if err != nil {
    				return nil, err
    			}
    		}
    		return r, nil
    	}
    	return nil, protocolError("unexpected response line")
    }
    

    结果转化

    reply.go 中提供各种方法格式化返回结果

    例如 String 方法

    func String(reply interface{}, err error) (string, error) {
    	if err != nil {
    		return "", err
    	}
    	switch reply := reply.(type) {
    	case []byte:
    		return string(reply), nil
    	case string:
    		return reply, nil
    	case nil:
    		return "", ErrNil
    	case Error:
    		return "", reply
    	}
    	return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
    }
    
  • 相关阅读:
    (10)时间
    (9)字符串
    (8)数组工具类
    (7)数学工具类
    (6)随机数
    (5)包装类
    (4)声明式接口和常用接口
    (3)java.lang.System
    (2)java.lang.Object
    (1)开篇
  • 原文地址:https://www.cnblogs.com/SLchuck/p/13964570.html
Copyright © 2011-2022 走看看