零件都准备好就可以组装起来了!
前几天分别完成了redis
, error
, log
的封装, 接下来就是依照API的input/output分别组合出各个API的内容。
API可以善用defer
来log API的进出资讯以及执行时间, 记录下总执行时间可以分析出API处理时长是否符合期望值, ex: 一句redis语法预期会是1ms, 打一个外部gRPC API要在50ms以内得到回应...等等, 纪录时间可以做为服务调校的依据, 当API流量超过预期时, 这些时间就会是API优化的参考值。
服务log下来的各项资讯及error可以透过ELK收集起来, 收集之後还可以依照讯息的等级决定是否转发到email或通讯软件, 目前通讯转体都有开放API提供介接转发讯息。
把处理行为都封装好之後, error 发生时也可以透过log清楚地知道是哪一个funcion发生问题, 不仅每支function行数变简洁, 对於问题发生时厘清查找范围也很有帮助!
组合出来的结果大致如下:
package main
import (
"context"
"errors"
"regexp"
"strconv"
"time"
coconutError "github.com/evelynocean/coconut/lib/error"
coconut_model "github.com/evelynocean/coconut/model"
coconut "github.com/evelynocean/coconut/pb"
coconut_redis "github.com/evelynocean/coconut/redis"
)
// Ping 提供服务运行确认
func (s *server) Ping(ctx context.Context, in *coconut.PingRequest) (r *coconut.Pong, err error) {
r = &coconut.Pong{
Pong: "pong",
}
return r, err
}
// 更新统计点数
func (s *server) UpdatePoints(ctx context.Context, in *coconut.PointsRequest) (r *coconut.RetResultStatus, err error) {
start := time.Now()
defer func() {
Logger.WithFields(map[string]interface{}{
"input": in,
"execute_time": time.Since(start).Seconds(),
"response": r,
}).Debugf("UpdatePoints")
// HandlerPanicRecover(&err)
}()
// TODO: input data check
if in.Level_1 == "" || in.Level_2 == "" || in.Level_3 == "" {
return nil, coconutError.ParseError(coconutError.ErrServer, errors.New("invalid parameter"))
}
r = &coconut.RetResultStatus{}
sets := &coconut_redis.KeySet{
Level1: in.Level_1,
Level2: in.Level_2,
Level3: in.Level_3,
UserName: in.UserName,
}
keys := coconut_redis.GetPointKey(sets)
limitSettings, err := coconut_model.GetLimit(s.ScyllaSession)
for idx, v := range keys {
limit := limitSettings[strconv.Itoa(idx)]
err = coconut_redis.PointSet(s.RedisClient, v, int(in.Point), time.Duration(30)*time.Second, limit)
if err != nil {
Logger.WithFields(map[string]interface{}{
"key": v,
"point": in.Point,
"limit": limit,
"time": time.Now().UnixNano(),
"err:": err.Error(),
}).Errorf("redis.PointSet")
return nil, coconutError.ParseError(coconutError.ErrRedis, err)
}
}
r = &coconut.RetResultStatus{
Success: true,
}
return r, nil
}
// 查询统计点数
func (s *server) GetPoints(ctx context.Context, in *coconut.GetPointsRequest) (r *coconut.RetPoints, err error) {
start := time.Now()
var data []*coconut.PointInfo
defer func() {
Logger.WithFields(map[string]interface{}{
"input": in,
"execute_time": time.Since(start).Seconds(),
"response": r,
}).Debugf("GetPoints")
// HandlerPanicRecover(&err)
}()
if in.Level_1 == "" || in.Level_2 == "" || in.Level_3 == "" {
return nil, coconutError.ParseError(coconutError.ErrServer, errors.New("invalid parameter"))
}
// 解析出各个层级
reg := regexp.MustCompile(`^LEVEL.*:(\w+)$`)
sets := &coconut_redis.KeySet{
Level1: in.Level_1,
Level2: in.Level_2,
Level3: in.Level_3,
}
keys := coconut_redis.GetPointKey(sets)
for _, v := range keys {
resultPoint, err := coconut_redis.PointGet(s.RedisClient, v)
if err != nil {
Logger.WithFields(map[string]interface{}{
"key": v,
"time": time.Now().UnixNano(),
"err:": err.Error(),
}).Errorf("redis.PointGet")
return nil, coconutError.ParseError(coconutError.ErrRedis, err)
}
if resultPoint > 0 {
matchSlice := reg.FindStringSubmatch(v)
d := &coconut.PointInfo{
Name: matchSlice[1],
Points: int32(resultPoint),
}
data = append(data, d)
}
}
r = &coconut.RetPoints{
Data: data,
}
return r, nil
}
<<: EP08 - 用 Terraform 建置 AWS RDS 服务(以 Aurora Postgres 为例)
我们接着继续开发 测验区 的部分 测验区 先来看看画面: 可以看到测试区除了上面的总分之外,我们有四...
终於跨入第 11 天,今天要来了解在 Go 里面我很不理解的一个型别 -- Pointer。 话不多...
HERE Fleet Telematics Advanced 资料集是一种 REST API,可让您...
甚麽是第三方支付? 第三方支付是指电子商务企业或是具实力及信用保障的独立机构,与银行之间建立一个中立...
相信各位看官们很熟悉各种Html的Events事件, 这篇呢~我们要透过上一篇所提到的State传入...