day 12 - API组装实作

零件都准备好就可以组装起来了!
前几天分别完成了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 为例)

>>:  F# 语言和你 SAY HELLO!!

【I Love Vue 】 Day 27 爱荷华博弈任务(八) - 测验画面2

我们接着继续开发 测验区 的部分 测验区 先来看看画面: 可以看到测试区除了上面的总分之外,我们有四...

Day11# Pointer

终於跨入第 11 天,今天要来了解在 Go 里面我很不理解的一个型别 -- Pointer。 话不多...

HERE API Example - 使用 Platform Data Extension REST API 显示邮递区号图层

HERE Fleet Telematics Advanced 资料集是一种 REST API,可让您...

企划实现(6)

甚麽是第三方支付? 第三方支付是指电子商务企业或是具实力及信用保障的独立机构,与银行之间建立一个中立...

【Day8】 将Function当成state传给子类别套用在事件上吧≖‿≖

相信各位看官们很熟悉各种Html的Events事件, 这篇呢~我们要透过上一篇所提到的State传入...