From e6f2bf51522b5ad4e2a171a44a0c446924ab4a9b Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 15 Apr 2016 09:41:10 +0800 Subject: [PATCH 1/2] Add app status. Signed-off-by: Bo-Yi Wu --- gorush.go | 3 +++ gorush/global.go | 3 ++- gorush/notification.go | 9 ++++++- gorush/notification_test.go | 4 +-- gorush/status.go | 51 ++++++++++++++++++++++++++++++++++++ gorush/status_test.go | 52 +++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 gorush/status.go create mode 100644 gorush/status_test.go diff --git a/gorush.go b/gorush.go index 3bdaf46..811e0a0 100644 --- a/gorush.go +++ b/gorush.go @@ -77,6 +77,7 @@ func main() { Message: *message, } + gorush.InitAppStatus() gorush.PushToAndroid(req) return @@ -103,6 +104,7 @@ func main() { Message: *message, } + gorush.InitAppStatus() gorush.InitAPNSClient() gorush.PushToIOS(req) @@ -113,6 +115,7 @@ func main() { gorush.LogError.Fatal(err) } + gorush.InitAppStatus() gorush.InitAPNSClient() gorush.InitWorkers(gorush.PushConf.Core.WorkerNum, gorush.PushConf.Core.QueueNum) gorush.RunHTTPServer() diff --git a/gorush/global.go b/gorush/global.go index 7688bc4..e7cd42c 100644 --- a/gorush/global.go +++ b/gorush/global.go @@ -18,5 +18,6 @@ var ( // LogAccess is log server request log LogAccess *logrus.Logger // LogError is log server error log - LogError *logrus.Logger + LogError *logrus.Logger + RushStatus StatusApp ) diff --git a/gorush/notification.go b/gorush/notification.go index e42bce1..65c6d8b 100644 --- a/gorush/notification.go +++ b/gorush/notification.go @@ -158,9 +158,11 @@ func queueNotification(req RequestPush) int { } QueueNotification <- notification - count++ + count += len(notification.Tokens) } + addTotalCount(int64(count)) + return count } @@ -276,6 +278,7 @@ func PushToIOS(req PushNotification) bool { // apns server error LogPush(FailedPush, token, req, err) isError = true + addIosError(1) continue } @@ -283,11 +286,13 @@ func PushToIOS(req PushNotification) bool { // error message: // ref: https://github.com/sideshow/apns2/blob/master/response.go#L14-L65 LogPush(FailedPush, token, req, errors.New(res.Reason)) + addIosError(1) continue } if res.Sent() { LogPush(SucceededPush, token, req, nil) + addIosSuccess(1) } } @@ -380,6 +385,8 @@ func PushToAndroid(req PushNotification) bool { } LogAccess.Debug(fmt.Sprintf("Android Success count: %d, Failure count: %d", res.Success, res.Failure)) + addAndroidSuccess(int64(res.Success)) + addAndroidError(int64(res.Failure)) for k, result := range res.Results { if result.Error != "" { diff --git a/gorush/notification_test.go b/gorush/notification_test.go index d91d6b4..e6fd10c 100644 --- a/gorush/notification_test.go +++ b/gorush/notification_test.go @@ -355,7 +355,7 @@ func TestSenMultipleNotifications(t *testing.T) { } count := queueNotification(req) - assert.Equal(t, 2, count) + assert.Equal(t, 3, count) } func TestDisabledAndroidNotifications(t *testing.T) { @@ -421,7 +421,7 @@ func TestDisabledIosNotifications(t *testing.T) { } count := queueNotification(req) - assert.Equal(t, 1, count) + assert.Equal(t, 2, count) } func TestMissingIosCertificate(t *testing.T) { diff --git a/gorush/status.go b/gorush/status.go new file mode 100644 index 0000000..cdca1e9 --- /dev/null +++ b/gorush/status.go @@ -0,0 +1,51 @@ +package gorush + +import ( + "sync/atomic" +) + +type StatusApp struct { + QueueMax int `json:"queue_max"` + QueueUsage int `json:"queue_usage"` + TotalCount int64 `json:"total_count"` + Ios IosStatus `json:"ios"` + Android AndroidStatus `json:"android"` +} + +type AndroidStatus struct { + PushSuccess int64 `json:"push_success"` + PushError int64 `json:"push_error"` +} + +type IosStatus struct { + PushSuccess int64 `json:"push_success"` + PushError int64 `json:"push_error"` +} + +func InitAppStatus() { + RushStatus.TotalCount = 0 + RushStatus.Ios.PushSuccess = 0 + RushStatus.Ios.PushError = 0 + RushStatus.Android.PushSuccess = 0 + RushStatus.Android.PushError = 0 +} + +func addTotalCount(count int64) { + atomic.AddInt64(&RushStatus.TotalCount, count) +} + +func addIosSuccess(count int64) { + atomic.AddInt64(&RushStatus.Ios.PushSuccess, count) +} + +func addIosError(count int64) { + atomic.AddInt64(&RushStatus.Ios.PushError, count) +} + +func addAndroidSuccess(count int64) { + atomic.AddInt64(&RushStatus.Android.PushSuccess, count) +} + +func addAndroidError(count int64) { + atomic.AddInt64(&RushStatus.Android.PushError, count) +} diff --git a/gorush/status_test.go b/gorush/status_test.go new file mode 100644 index 0000000..0aa8017 --- /dev/null +++ b/gorush/status_test.go @@ -0,0 +1,52 @@ +package gorush + +import ( + "github.com/stretchr/testify/assert" + "sync/atomic" + "testing" +) + +func TestAddTotalCount(t *testing.T) { + InitAppStatus() + addTotalCount(1000) + + val := atomic.LoadInt64(&RushStatus.TotalCount) + + assert.Equal(t, int64(1000), val) +} + +func TestAddIosSuccess(t *testing.T) { + InitAppStatus() + addIosSuccess(1000) + + val := atomic.LoadInt64(&RushStatus.Ios.PushSuccess) + + assert.Equal(t, int64(1000), val) +} + +func TestAddIosError(t *testing.T) { + InitAppStatus() + addIosError(1000) + + val := atomic.LoadInt64(&RushStatus.Ios.PushError) + + assert.Equal(t, int64(1000), val) +} + +func TestAndroidSuccess(t *testing.T) { + InitAppStatus() + addAndroidSuccess(1000) + + val := atomic.LoadInt64(&RushStatus.Android.PushSuccess) + + assert.Equal(t, int64(1000), val) +} + +func TestAddAndroidError(t *testing.T) { + InitAppStatus() + addAndroidError(1000) + + val := atomic.LoadInt64(&RushStatus.Android.PushError) + + assert.Equal(t, int64(1000), val) +} From a346f29f95661aabd6b4a076b607546bb1b01955 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Fri, 15 Apr 2016 10:24:39 +0800 Subject: [PATCH 2/2] Support success or failure of notification counts information. Signed-off-by: Bo-Yi Wu --- README.md | 74 +++++++++++++++++++++++++++++++++++++++---- config/config.yml | 3 +- gorush/config.go | 4 ++- gorush/server.go | 1 + gorush/server_test.go | 17 ++++++++-- gorush/status.go | 16 ++++++++++ 6 files changed, 104 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index b4d9078..b0f0946 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ A push notification server using [Gin](https://github.com/gin-gonic/gin) framewo * Support zero downtime restarts for go servers using [endless](https://github.com/fvbock/endless). * Support [HTTP/2](https://http2.github.io/) or HTTP/1.1 protocol. * Support notification queue and multiple workers. +* Support `/api/stat/app` show notification success and failure counts. See the [YAML config example](config/config.yml): @@ -35,7 +36,8 @@ core: api: push_uri: "/api/push" - stat_go_uri: "/api/status" + stat_go_uri: "/api/stat/go" + stat_app_uri: "/api/stat/app" android: enabled: true @@ -101,19 +103,79 @@ Please make sure your [config.yml](config/config.yml) exist. Default port is `80 $ gorush -c config.yml ``` -Test status of api server using [httpie](https://github.com/jkbrzt/httpie) tool: +Get go status of api server using [httpie](https://github.com/jkbrzt/httpie) tool: ```bash -$ http -v --verify=no --json GET https://localhost:8088/api/status +$ http -v --verify=no --json GET https://localhost:8088/api/stat/go ``` ## Web API -gorush support the following API. +Gorush support the following API. -* **GET** `/api/status` Golang cpu, memory, gc, etc information. Thanks for [golang-stats-api-handler](https://github.com/fukata/golang-stats-api-handler). +* **GET** `/api/stat/go` Golang cpu, memory, gc, etc information. Thanks for [golang-stats-api-handler](https://github.com/fukata/golang-stats-api-handler). +* **GET** `/api/stat/app` show notification success and failure counts. * **POST** `/api/push` push ios and android notifications. +### GET /api/stat/go + +Golang cpu, memory, gc, etc information. Response with `200` http status code. + +```json +{ + time: 1460686815848046600, + go_version: "go1.6.1", + go_os: "darwin", + go_arch: "amd64", + cpu_num: 4, + goroutine_num: 15, + gomaxprocs: 4, + cgo_call_num: 1, + memory_alloc: 7455192, + memory_total_alloc: 8935464, + memory_sys: 12560632, + memory_lookups: 17, + memory_mallocs: 31426, + memory_frees: 11772, + memory_stack: 524288, + heap_alloc: 7455192, + heap_sys: 8912896, + heap_idle: 909312, + heap_inuse: 8003584, + heap_released: 0, + heap_objects: 19654, + gc_next: 9754725, + gc_last: 1460686815762559700, + gc_num: 2, + gc_per_second: 0, + gc_pause_per_second: 0, + gc_pause: [ + 0.326576, + 0.227096 + ] +} +``` + +### GET /api/stat/app + +Get success or failure of notification counts information. + +```json +{ + queue_max: 8192, + queue_usage: 0, + total_count: 77, + ios: { + push_success: 19, + push_error: 38 + }, + android: { + push_success: 10, + push_error: 10 + } + } +``` + ### POST /api/push Simple send iOS notification example, the `platform` value is `1`: @@ -356,7 +418,7 @@ $ docker run -name gorush -p 80:8088 appleboy/gorush Testing your gorush server. ```bash -$ http -v --verify=no --json GET http://your.docker.host/api/status +$ http -v --verify=no --json GET http://your.docker.host/api/stat/go ``` ![statue screenshot](screenshot/status.png) diff --git a/config/config.yml b/config/config.yml index b512f2d..570a329 100644 --- a/config/config.yml +++ b/config/config.yml @@ -10,7 +10,8 @@ core: api: push_uri: "/api/push" - stat_go_uri: "/api/status" + stat_go_uri: "/api/stat/go" + stat_app_uri: "/api/stat/app" android: enabled: true diff --git a/gorush/config.go b/gorush/config.go index 9298e34..7b6ee8f 100644 --- a/gorush/config.go +++ b/gorush/config.go @@ -31,6 +31,7 @@ type SectionCore struct { type SectionAPI struct { PushURI string `yaml:"push_uri"` StatGoURI string `yaml:"stat_go_uri"` + StatAppURI string `yaml:"stat_app_uri"` } // SectionAndroid is sub seciont of config. @@ -72,7 +73,8 @@ func BuildDefaultPushConf() ConfYaml { // Api conf.API.PushURI = "/api/push" - conf.API.StatGoURI = "/api/status" + conf.API.StatGoURI = "/api/stat/go" + conf.API.StatAppURI = "/api/stat/app" // Android conf.Android.Enabled = false diff --git a/gorush/server.go b/gorush/server.go index 3a41f84..6bcd617 100644 --- a/gorush/server.go +++ b/gorush/server.go @@ -68,6 +68,7 @@ func routerEngine() *gin.Engine { r.Use(LogMiddleware()) r.GET(PushConf.API.StatGoURI, api.StatusHandler) + r.GET(PushConf.API.StatAppURI, appStatusHandler) r.POST(PushConf.API.PushURI, pushHandler) r.GET("/", rootHandler) diff --git a/gorush/server_test.go b/gorush/server_test.go index 8855683..b90757e 100644 --- a/gorush/server_test.go +++ b/gorush/server_test.go @@ -36,7 +36,7 @@ func TestRunNormalServer(t *testing.T) { time.Sleep(5 * time.Millisecond) assert.Error(t, RunHTTPServer()) - gofight.TestRequest(t, "http://localhost:8088/api/status") + gofight.TestRequest(t, "http://localhost:8088/api/stat/go") } func TestRunTLSServer(t *testing.T) { @@ -76,12 +76,12 @@ func TestRootHandler(t *testing.T) { }) } -func TestAPIStatusHandler(t *testing.T) { +func TestAPIStatusGoHandler(t *testing.T) { initTest() r := gofight.New() - r.GET("/api/status"). + r.GET("/api/stat/go"). Run(routerEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) { data := []byte(r.Body.String()) @@ -92,6 +92,17 @@ func TestAPIStatusHandler(t *testing.T) { }) } +func TestAPIStatusAppHandler(t *testing.T) { + initTest() + + r := gofight.New() + + r.GET("/api/stat/app"). + Run(routerEngine(), func(r gofight.HTTPResponse, rq gofight.HTTPRequest) { + assert.Equal(t, http.StatusOK, r.Code) + }) +} + func TestMissingNotificationsParameter(t *testing.T) { initTest() diff --git a/gorush/status.go b/gorush/status.go index cdca1e9..319022a 100644 --- a/gorush/status.go +++ b/gorush/status.go @@ -1,6 +1,8 @@ package gorush import ( + "github.com/gin-gonic/gin" + "net/http" "sync/atomic" ) @@ -49,3 +51,17 @@ func addAndroidSuccess(count int64) { func addAndroidError(count int64) { atomic.AddInt64(&RushStatus.Android.PushError, count) } + +func appStatusHandler(c *gin.Context) { + result := StatusApp{} + + 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) + + c.JSON(http.StatusOK, result) +}