最近在使用nginx+lua+redis做一个系统,来支撑高并发高访问量的应用。开发时突然想到golang是不是也可以达到同样的效果。于是写了个简单的代码对比一下。
具体就不多做介绍了,网上很多关于nginx+lua+redis构建高并发应用的介绍。我使用的是openresty+lua+redis。
先贴下测试结果,机器就是2013年新出的低配air——(1.3 GHz Intel Core i5, 4 GB 1600 MHz DDR3), 命令:
ab -n 1000 -c 100 http://localhost:8880/
openresty+lua+redis: Concurrency Level: 100 Time taken for tests: 0.458 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 689000 bytes HTML transferred: 533000 bytes Requests per second: 2183.67 [#/sec] (mean) Time per request: 45.794 [ms] (mean) Time per request: 0.458 [ms] (mean, across all concurrent requests) Transfer rate: 1469.29 [Kbytes/sec] received
golang+redis: Concurrency Level: 100 Time taken for tests: 0.503 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 650000 bytes HTML transferred: 532000 bytes Requests per second: 1988.22 [#/sec] (mean) Time per request: 50.296 [ms] (mean) Time per request: 0.503 [ms] (mean, across all concurrent requests) Transfer rate: 1262.05 [Kbytes/sec] received
lua代码:
-- redis 配置 local params = { host='127.0.0.1', port = 6379, } local red = redis:new() local ok, err = red:connect(params.host, params.port) if not ok then ngx.say("failed to connect: ", err) return end local position_key = ngx.var.position_key local content = red:get(position_key) ngx.print(content)
golang代码 :
package main import ( "fmt" "github.com/garyburd/redigo/redis" "log" "net/http" "time" ) func getConn() (redis.Conn, error) { conn, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second) if err != nil { fmt.Println(err) } return conn, err } func indexHandler(w http.ResponseWriter, r *http.Request) { conn, err := getConn() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } result, err := conn.Do("get", "content_1") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintf(w, "Hello, %q", result) } func main() { http.HandleFunc("/", indexHandler) err := http.ListenAndServe(":8880", nil) if err != nil { log.Fatal("ListenAndServe: ", err.Error()) } }
经过多次压测之后发现,nginx + lua + redis的组合确实高效,golang + redis的方案其实也差不了多少。相对于整个系统从开发到部署的方式来说,golang可能更合适,更符合开发的习惯,毕竟nginx + lua 这种方案开发和测试都略显别扭。
上次测试完之后,觉得这个代码还有提高的空间,于是查了下怎么在golang中使用redis连接池(其实就是redigo的使用),还有lua中怎么使用redis连接池(其实就是rest.redis的使用)。
先上结果:
openresty + lua + redis Concurrency Level: 100 Time taken for tests: 0.284 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 687000 bytes HTML transferred: 531000 bytes Requests per second: 3522.03 [#/sec] (mean) Time per request: 28.393 [ms] (mean) Time per request: 0.284 [ms] (mean, across all concurrent requests) Transfer rate: 2362.93 [Kbytes/sec] received
再看golang:
golang + redis Concurrency Level: 100 Time taken for tests: 0.327 seconds Complete requests: 1000 Failed requests: 0 Total transferred: 650000 bytes HTML transferred: 532000 bytes Requests per second: 3058.52 [#/sec] (mean) Time per request: 32.696 [ms] (mean) Time per request: 0.327 [ms] (mean, across all concurrent requests) Transfer rate: 1941.44 [Kbytes/sec] received
lua代码:
-- redis 配置 local params = { host='127.0.0.1', port = 6379, } local red = redis:new() local ok, err = red:connect(params.host, params.port) if not ok then ngx.say("failed to connect: ", err) return end local position_key = ngx.var.position_key local content = red:get(position_key) ngx.print(content) local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.say("failed to set keepalive: ", err) return end
golang代码:
package main import ( "flag" "fmt" "github.com/garyburd/redigo/redis" "log" "net/http" "runtime" "time" ) var ( pool *redis.Pool redisServer = flag.String("redisServer", ":6379", "") ) func indexHandler(w http.ResponseWriter, r *http.Request) { t0 := time.Now() conn := pool.Get() t1 := time.Now() fmt.Printf("The call took %v to run.\n", t1.Sub(t0)) defer conn.Close() result, err := conn.Do("get", "content_1") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } fmt.Fprintf(w, "Hello, %q", result) } func newPool(server string) *redis.Pool { return &redis.Pool{ MaxIdle: 3, IdleTimeout: 240 * time.Second, Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", server) if err != nil { return nil, err } return c, err }, TestOnBorrow: func(c redis.Conn, t time.Time) error { _, err := c.Do("PING") return err }, } } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) flag.Parse() pool = newPool(*redisServer) http.HandleFunc("/", indexHandler) err := http.ListenAndServe(":8880", nil) if err != nil { log.Fatal("ListenAndServe: ", err.Error()) } }
golang中除了添加了线程池,还设置了cpu核数。
不过这个测试并不十分严谨,redis,nginx,golang http server,ab压测都在一台机器,相互之间会有影响。有兴趣的可以自己分开部署测试下。