什麼是socket編程? socket編程是計算機PC機器上2個程序經過一個雙向的通訊鏈接實現數據的交互,這個鏈接的一端就是一個socket。socket的翻譯意思上還有個插座的概念,其實,也能夠很形象的比喻爲插座插上去了就有通電了(網絡通了)。socket編程其實做爲UNIX系統的進程間通訊機制,一般稱爲「套接字」,用來描述IP地址和端口的集合,在unix系統下是一個通訊的句柄(文件描述符,由於UNIX下全部都是文件)。git
UNIX socket編程的流程大概以下:github
go語言的基礎包中,網絡包net包含了不少網絡I/O,TCP/IP,UDP,域名解析,Unix 域套接字等。redis
雖然提供的都是直接面對很底層的網絡I/O訪問,可是主要的TCP通訊的接口也是封裝得比較簡單,好用,包括有:數據庫
a. 監聽端口編程
b. 接收客戶端的連接api
c. 建立goroutine,處理該連接緩存
a. 創建與服務器的連接服務器
b. 進行數據收發網絡
c. 關閉連接數據結構
package main import ( "fmt" "net" ) func main() { fmt.Println("start server...") listen, err := net.Listen("tcp", "0.0.0.0:50000") if err != nil { fmt.Println("listen failed, err:", err) return } for { conn, err := listen.Accept() if err != nil { fmt.Println("accept failed, err:", err) continue } go process(conn) } } func process(conn net.Conn) { defer conn.Close() for { buf := make([]byte, 512) n, err := conn.Read(buf) if err != nil { fmt.Println("read err:", err) return } fmt.Printf(string(buf[0:n])) } }
package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { conn, err := net.Dial("tcp", "localhost:50000") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() inputReader := bufio.NewReader(os.Stdin) for { input, _ := inputReader.ReadString('\n') trimmedInput := strings.Trim(input, "\r\n") if trimmedInput == "Q" { return } _, err = conn.Write([]byte(trimmedInput)) if err != nil { return } } }
package main import ( "fmt" "io" "net" ) func main() { conn, err := net.Dial("tcp", "www.baidu.com:80") if err != nil { fmt.Println("Error dialing", err.Error()) return } defer conn.Close() msg := "GET / HTTP/1.1\r\n" msg += "Host:www.baidu.com\r\n" msg += "Connection:keep-alive\r\n" // msg += "User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\r\n" msg += "\r\n\r\n" //io.WriteString(os.Stdout, msg) n, err := io.WriteString(conn, msg) if err != nil { fmt.Println("write string failed, ", err) return } fmt.Println("send to baidu.com bytes:", n) buf := make([]byte, 4096) for { count, err := conn.Read(buf) fmt.Println("count:", count, "err:", err) if err != nil { break } fmt.Println(string(buf[0:count])) } }
redis是個開源的高性能的key-value的內存數據庫,能夠把它當成遠程的數據結構。支持的value類型很是多,好比string、list(鏈表)、set(集合)、hash表等等。redis性能很是高,單機可以達到15w qps,一般適合作緩存。
支持更多數據類型
和Memcached相似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set 有序集合)和hash(哈希類型)。[1]
支持複雜操做
這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,Redis支持各類不一樣方式的排序。[2]
支持主從同步。
與memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是Redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。數據能夠從主服務器向任意數量的從服務器上同步,從服務器能夠是關聯其餘從服務器的主服務器。這使得Redis可執行單層樹複製。從盤能夠有意無心的對數據進行寫操做。因爲徹底實現了發佈/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發佈記錄。同步對讀取操做的可擴展性和數據冗餘頗有幫助。
redis
中,再由redis
寫入數據庫,減輕同時寫入壓力。redis
來優化,在新聞錄入的時候將標題、時間和來源寫入redis
中,客戶端訪問時,能夠從內存中一次性取出當天熱單新聞列表,極大地提升請求速度和節約了服務器開銷。redis
並同時設置key
過時時間,這樣後臺api
過濾請求時,就能夠從內存中讀取用戶信息,並且redis
的過時機制,自然支持用戶身份有效期校驗,用起來十分方便。INCRBY
命令原生支持。Redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合能夠對關係數據庫起到很好的補充做用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。Redis的官網地址,很是好記,是redis.io。目前,Vmware在資助着Redis項目的開發和維護。
Redis主要有五種基本數據結構,知足了絕大多數緩存結構的須要,若是你在使用一種結構存儲時感受彆扭時,頗有多是選錯了存儲結構,能夠考慮一下其餘結構的正確實現。
string
,能夠考慮用Hash
LPUSH
、LPOP
、RPUSH
、RPOP
等指令組合能夠實現棧和隊列操做。Score
值來決定元素排序,適合用戶排名這樣的業務場景。redigo是GO語言的一個redis客戶端實現。項目位於https://github.com/garyburd/redigo
redigo沒有其餘依賴項,能夠直接經過go get進行安裝
go get github.com/garyburd/redigo/redis
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() }
Redigo Pool 結構維護一個 Redis 鏈接池。應用程序調用 Get 方法從池中獲取鏈接,並使用鏈接的 Close 方法將鏈接的資源返回到池中。通常咱們在系統初始化時聲明一個全局鏈接池,而後在須要操做redis
時得到鏈接,執行指令。
pool := &redis.Pool{ MaxIdle: 3, /*最大的空閒鏈接數*/ MaxActive: 8, /*最大的激活鏈接數*/ Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", "連接地址,例如127.0.0.1:6379", redis.DialPassword("密碼")) if err != nil { return nil, err } return c, nil }, } c:=pool.Get() defer c.Close()
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) var pool *redis.Pool func init() { pool = &redis.Pool{ MaxIdle: 16, /* 最大的空閒鏈接數 */ MaxActive: 0, /* 最大的激活鏈接數 */ IdleTimeout: 300, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", "localhost:6379", redis.DialPassword("0000")) if err != nil { return nil, err } return c, nil }, } } func main() { c := pool.Get() defer c.Close() _, err := c.Do("Set", "abc", 100) if err != nil { fmt.Println(err) return } r, err := redis.Int(c.Do("Get", "abc")) if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) pool.Close() }
查看源碼,發現Conn
接口有一個執行 Redis 命令的通用方法:
//gomodule/redigo/redis/redis.go // Conn represents a connection to a Redis server. 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) }
http://redis.io/commands 中的 Redis 命令參考列出了可用的命令。do
的參數和redis-cli
命令參數格式一致,好比SET key value EX 360
對應函數調用爲Do("SET", "key", "value","EX",360)
,經常使用的命令示例有:
c := pool.Get() defer c.Close() //存值, _, err := c.Do("SET", "key", "value") //設置過時時間 _, err := c.Do("SET", "key", "value","EX",360) //存int _, err := c.Do("SET", "key", 2) //取值 v,err:=redis.String(c.Do("GET","key")) bytes, err := redis.Bytes(c.Do("GET", "key"))
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { var p *int var a int p = &a *p = 0 c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("Set", "abc", 100) if err != nil { fmt.Println(err) return } r, err := redis.Int(c.Do("Get", "abc")) if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("MSet", "abc", 100, "efg", 300) if err != nil { fmt.Println(err) return } r, err := redis.Ints(c.Do("MGet", "abc", "efg")) if err != nil { fmt.Println("get abc failed,", err) return } for _, v := range r { fmt.Println(v) } }
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("HSet", "books", "abc", 100) if err != nil { fmt.Println(err) return } r, err := redis.Int(c.Do("HGet", "books", "abc")) if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("lpush", "book_list", "abc", "ceg", 300) if err != nil { fmt.Println(err) return } r, err := redis.String(c.Do("lpop", "book_list")) if err != nil { fmt.Println("get abc failed,", err) return } fmt.Println(r) }
package main import ( "fmt" "github.com/garyburd/redigo/redis" ) func main() { c, err := redis.Dial("tcp", "localhost:6379") if err != nil { fmt.Println("conn redis failed,", err) return } defer c.Close() _, err = c.Do("expire", "abc", 10) if err != nil { fmt.Println(err) return } }