Merge pull request #62 from appleboy/redis

Fixed #61 Support redis engine
This commit is contained in:
Bo-Yi Wu 2016-04-22 10:56:33 -05:00
commit f14d42d9a8
9 changed files with 277 additions and 15 deletions

View File

@ -3,14 +3,15 @@ sudo: required
language: go
go:
- 1.3
- 1.4
- 1.5
- 1.6
- 1.6.1
- 1.6.2
- tip
services:
- docker
- redis
env:
global:

View File

@ -21,6 +21,7 @@ A push notification server using [Gin](https://github.com/gin-gonic/gin) framewo
* Support notification queue and multiple workers.
* Support `/api/stat/app` show notification success and failure counts.
* Support `/api/config` show your yml config.
* Support store app stat to [redis](http://redis.io/) or memory.
See the [YAML config example](config/config.yml):
@ -57,6 +58,13 @@ log:
access_level: "debug"
error_log: "stderr" # stderr: output to console, or define log path like "log/error_log"
error_level: "error"
stat:
engine: "memory" # support memory or redis
redis:
addr: "localhost:6379"
password: ""
db: 0
```
## Basic Usage

View File

@ -30,3 +30,10 @@ log:
access_level: "debug"
error_log: "stderr"
error_level: "error"
stat:
engine: "memory"
redis:
addr: "localhost:6379"
password: ""
db: 0

View File

@ -7,6 +7,6 @@ RUN mkdir -p /tmp/build
Add build.tar.gz /tmp/build/
WORKDIR /tmp/build
RUN go get -v -d
RUN GOOS=linux GOARCH=amd64 go build -ldflags="-w" -o bin/gorush gorush.go
RUN GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/gorush gorush.go
CMD tar -C bin -czf - gorush

View File

@ -13,6 +13,7 @@ type ConfYaml struct {
Android SectionAndroid `yaml:"android"`
Ios SectionIos `yaml:"ios"`
Log SectionLog `yaml:"log"`
Stat SectionStat `yaml:stat`
}
// SectionCore is sub seciont of config.
@ -58,6 +59,19 @@ type SectionLog struct {
ErrorLevel string `yaml:"error_level"`
}
// SectionStat is sub seciont of config.
type SectionStat struct {
Engine string `yaml:"service"`
Redis SectionRedis `yaml:"redis"`
}
// SectionRedis is sub seciont of config.
type SectionRedis struct {
Addr string `yaml:"addr"`
Password string `yaml:"password"`
DB int64 `yaml:"db"`
}
// BuildDefaultPushConf is default config setting.
func BuildDefaultPushConf() ConfYaml {
var conf ConfYaml
@ -95,6 +109,11 @@ func BuildDefaultPushConf() ConfYaml {
conf.Log.ErrorLog = "stderr"
conf.Log.ErrorLevel = "error"
conf.Stat.Engine = "memory"
conf.Stat.Redis.Addr = "localhost:6379"
conf.Stat.Redis.Password = ""
conf.Stat.Redis.DB = 0
return conf
}

View File

@ -18,3 +18,12 @@ const (
// FailedPush is log block
FailedPush = "failed-push"
)
// Stat variable for redis
const (
gorushTotalCount = "gorush-total-count"
gorushIosSuccess = "gorush-ios-success-count"
gorushIosError = "gorush-ios-error-count"
gorushAndroidSuccess = "gorush-android-success-count"
gorushAndroidError = "gorush-android-error-count"
)

View File

@ -4,6 +4,7 @@ import (
"crypto/tls"
"github.com/Sirupsen/logrus"
apns "github.com/sideshow/apns2"
"gopkg.in/redis.v3"
)
var (
@ -21,4 +22,6 @@ var (
LogError *logrus.Logger
// RushStatus is notification status
RushStatus StatusApp
// RedisClient is global variable for redis
RedisClient *redis.Client
)

View File

@ -2,7 +2,9 @@ package gorush
import (
"github.com/gin-gonic/gin"
"gopkg.in/redis.v3"
"net/http"
"strconv"
"sync/atomic"
)
@ -27,8 +29,14 @@ type IosStatus struct {
PushError int64 `json:"push_error"`
}
// InitAppStatus for initialize app status
func InitAppStatus() {
func getRedisInt64Result(key string) int64 {
val, _ := RedisClient.Get(key).Result()
count, _ := strconv.ParseInt(val, 10, 64)
return count
}
func initApp() {
RushStatus.TotalCount = 0
RushStatus.Ios.PushSuccess = 0
RushStatus.Ios.PushError = 0
@ -36,24 +44,168 @@ func InitAppStatus() {
RushStatus.Android.PushError = 0
}
func initRedis() error {
RedisClient = redis.NewClient(&redis.Options{
Addr: PushConf.Stat.Redis.Addr,
Password: PushConf.Stat.Redis.Password,
DB: PushConf.Stat.Redis.DB,
})
_, err := RedisClient.Ping().Result()
if err != nil {
// redis server error
LogError.Error("Can't connect redis server: " + err.Error())
return err
}
RushStatus.TotalCount = getTotalCount()
RushStatus.Ios.PushSuccess = getIosSuccess()
RushStatus.Ios.PushError = getIosError()
RushStatus.Android.PushSuccess = getAndroidSuccess()
RushStatus.Android.PushError = getAndroidError()
return nil
}
// InitAppStatus for initialize app status
func InitAppStatus() {
switch PushConf.Stat.Engine {
case "memory":
initApp()
case "redis":
initRedis()
default:
initApp()
}
}
func addTotalCount(count int64) {
atomic.AddInt64(&RushStatus.TotalCount, count)
switch PushConf.Stat.Engine {
case "memory":
atomic.AddInt64(&RushStatus.TotalCount, count)
case "redis":
RedisClient.Set(gorushTotalCount, strconv.Itoa(int(count)), 0)
default:
atomic.AddInt64(&RushStatus.TotalCount, count)
}
}
func addIosSuccess(count int64) {
atomic.AddInt64(&RushStatus.Ios.PushSuccess, count)
switch PushConf.Stat.Engine {
case "memory":
atomic.AddInt64(&RushStatus.Ios.PushSuccess, count)
case "redis":
RedisClient.Set(gorushIosSuccess, strconv.Itoa(int(count)), 0)
default:
atomic.AddInt64(&RushStatus.Ios.PushSuccess, count)
}
}
func addIosError(count int64) {
atomic.AddInt64(&RushStatus.Ios.PushError, count)
switch PushConf.Stat.Engine {
case "memory":
atomic.AddInt64(&RushStatus.Ios.PushError, count)
case "redis":
RedisClient.Set(gorushIosError, strconv.Itoa(int(count)), 0)
default:
atomic.AddInt64(&RushStatus.Ios.PushError, count)
}
}
func addAndroidSuccess(count int64) {
atomic.AddInt64(&RushStatus.Android.PushSuccess, count)
switch PushConf.Stat.Engine {
case "memory":
atomic.AddInt64(&RushStatus.Android.PushSuccess, count)
case "redis":
RedisClient.Set(gorushAndroidSuccess, strconv.Itoa(int(count)), 0)
default:
atomic.AddInt64(&RushStatus.Android.PushSuccess, count)
}
}
func addAndroidError(count int64) {
atomic.AddInt64(&RushStatus.Android.PushError, count)
switch PushConf.Stat.Engine {
case "memory":
atomic.AddInt64(&RushStatus.Android.PushError, count)
case "redis":
RedisClient.Set(gorushAndroidError, strconv.Itoa(int(count)), 0)
default:
atomic.AddInt64(&RushStatus.Android.PushError, count)
}
}
func getTotalCount() int64 {
var count int64
switch PushConf.Stat.Engine {
case "memory":
count = atomic.LoadInt64(&RushStatus.TotalCount)
case "redis":
count = getRedisInt64Result(gorushAndroidError)
default:
count = atomic.LoadInt64(&RushStatus.TotalCount)
}
return count
}
func getIosSuccess() int64 {
var count int64
switch PushConf.Stat.Engine {
case "memory":
count = atomic.LoadInt64(&RushStatus.Ios.PushSuccess)
case "redis":
count = getRedisInt64Result(gorushAndroidError)
default:
count = atomic.LoadInt64(&RushStatus.Ios.PushSuccess)
}
return count
}
func getIosError() int64 {
var count int64
switch PushConf.Stat.Engine {
case "memory":
count = atomic.LoadInt64(&RushStatus.Ios.PushError)
case "redis":
count = getRedisInt64Result(gorushAndroidError)
default:
count = atomic.LoadInt64(&RushStatus.Ios.PushError)
}
return count
}
func getAndroidSuccess() int64 {
var count int64
switch PushConf.Stat.Engine {
case "memory":
count = atomic.LoadInt64(&RushStatus.Android.PushSuccess)
case "redis":
count = getRedisInt64Result(gorushAndroidError)
default:
count = atomic.LoadInt64(&RushStatus.Android.PushSuccess)
}
return count
}
func getAndroidError() int64 {
var count int64
switch PushConf.Stat.Engine {
case "memory":
count = atomic.LoadInt64(&RushStatus.Android.PushError)
case "redis":
count = getRedisInt64Result(gorushAndroidError)
default:
count = atomic.LoadInt64(&RushStatus.Android.PushError)
}
return count
}
func appStatusHandler(c *gin.Context) {
@ -61,11 +213,11 @@ func appStatusHandler(c *gin.Context) {
result.QueueMax = cap(QueueNotification)
result.QueueUsage = len(QueueNotification)
result.TotalCount = atomic.LoadInt64(&RushStatus.TotalCount)
result.Ios.PushSuccess = atomic.LoadInt64(&RushStatus.Ios.PushSuccess)
result.Ios.PushError = atomic.LoadInt64(&RushStatus.Ios.PushError)
result.Android.PushSuccess = atomic.LoadInt64(&RushStatus.Android.PushSuccess)
result.Android.PushError = atomic.LoadInt64(&RushStatus.Android.PushError)
result.TotalCount = getTotalCount()
result.Ios.PushSuccess = getIosSuccess()
result.Ios.PushError = getIosError()
result.Android.PushSuccess = getAndroidSuccess()
result.Android.PushError = getAndroidError()
c.JSON(http.StatusOK, result)
}

View File

@ -50,3 +50,66 @@ func TestAddAndroidError(t *testing.T) {
assert.Equal(t, int64(1000), val)
}
func TestRedisServerSuccess(t *testing.T) {
PushConf.Stat.Redis.Addr = "localhost:6379"
err := initRedis()
assert.NoError(t, err)
}
func TestRedisServerError(t *testing.T) {
PushConf.Stat.Redis.Addr = "localhost:6370"
err := initRedis()
assert.Error(t, err)
}
func TestStatForRedisEngine(t *testing.T) {
var val int64
PushConf.Stat.Engine = "redis"
PushConf.Stat.Redis.Addr = "localhost:6379"
InitAppStatus()
addTotalCount(1000)
addIosSuccess(1000)
addIosError(1000)
addAndroidSuccess(1000)
addAndroidError(1000)
val = getTotalCount()
assert.Equal(t, int64(1000), val)
val = getIosSuccess()
assert.Equal(t, int64(1000), val)
val = getIosError()
assert.Equal(t, int64(1000), val)
val = getAndroidSuccess()
assert.Equal(t, int64(1000), val)
val = getAndroidError()
assert.Equal(t, int64(1000), val)
}
func TestDefaultEngine(t *testing.T) {
var val int64
PushConf.Stat.Engine = "test"
InitAppStatus()
addTotalCount(1000)
addIosSuccess(1000)
addIosError(1000)
addAndroidSuccess(1000)
addAndroidError(1000)
val = getTotalCount()
assert.Equal(t, int64(1000), val)
val = getIosSuccess()
assert.Equal(t, int64(1000), val)
val = getIosError()
assert.Equal(t, int64(1000), val)
val = getAndroidSuccess()
assert.Equal(t, int64(1000), val)
val = getAndroidError()
assert.Equal(t, int64(1000), val)
}