-
Notifications
You must be signed in to change notification settings - Fork 2
API设计
Shawn Clovie edited this page Dec 31, 2019
·
5 revisions
目录结构与接口以实际代码为准
|-- api 接口文档
|-- cmd 示例app,package main
| |-- config_rock.yaml
| \-- main.go
|-- docs 文档
|-- test 测试代码
|-- log Console与本地文件Log,package log
|-- middlewares 各种中间件
|-- rock package rock
| |-- Application.go
| |-- Config.go
| \-- ...
|-- CHANGELOG.md
|-- README.md
|-- Makefile
|-- go.mod
\-- ...
...
package rock
// 路由处理函数,执行完成后需调用ctx.Next(),除非不希望后续处理器生效
type Handler func(ctx iris.Context)
type Application interface {
// 应用名称,来自rock config中的app_name
Name()
// 载入rock config,以初始化各个内部模块
// 自动添加用于异常恢复的recover和sentry中间件、记录基础metric信息的中间件
InitWithRockConfig(path string) error
// 取得iris实例,以注册控制器、中间件等
Iris() *iris.Application
// 建立服务,以此注册各种请求的handler
NewService(name, path string) *Service
// 建立服务组以代理iris的Party功能
NewServiceGroup(name, path string) *ServiceGroup
// 启动服务,host可包含端口号,如省略域名或ip将与0.0.0.0等效
// 将会检查没有通过
Run(host string, conf ...iris.Configurator)
}
func NewApplication() Application
// implementation of Application
type application struct {
name string
iris *iris.Application
rockConfig map[string]interface{}
}
type Service struct {
app *application
name string
}
func (s *Service) Get(fn func(iris.Context)) *Service
func (s *Service) Post(fn func(iris.Context)) *Service
func (s *Service) Put(fn func(iris.Context)) *Service
func (s *Service) Option(fn func(iris.Context)) *Service
func (s *Service) Delete(fn func(iris.Context)) *Service
type ServiceGroup struct {
app *application
party iris.Party
name string
path string
services map[string]*Service
handlerStatus map[string]bool
}
func (g *ServiceGroup) Use(mw ...iris.Handler) *ServiceGroup
func (g *ServiceGroup) NewService(name, path string) *Service
func (g *ServiceGroup) NewServiceGroup(name, path string) *ServiceGroup
var config map[string]interface{}
var loggers map[string]*log.Logger
// 用默认的logger防止调用Logger(name)时对应logger不存在而得到空指针
var defaultLogger log.Logger
// 取得在Application.Init初始化过的该名称对应的Logger
func Logger(name string) *Logger
// 注册PanicHandler,由预定义中间件捕捉错误后调用
func SetPanicHandler(fn func(ctx iris.Context, err error))Config一方面作为加载json或yaml的工具,另一方面作为内部模块的数据源。
package rock
// 载入多个配置文件,遇到任何错误将立即返回
func ImportConfigFromPaths(paths ...string) error
// 载入指定目录下的所有文件(不递归)
// 文件将按照文件名作为map中的key,如app.yaml的内容将保存至data["app"]
func ImportConfigFilesFromDirectory(dir string) error
// 按以.分隔的keyPath查找配置
func ConfigIn(keyPath string) interface{}
// 载入的所有配置
func Config() map[string]interface{}
// 载入配置文件,支持json与yaml
// 如文件为空内容或不存在,将返回错误
func LoadConfigFromPath(path string, into interface{}) errorstdOut与本地文件输出。
package log
type Level string
const (
LevelFatal Level = "fatal"
LevelError Level = "error"
LevelWarn Level = "warn"
LevelInfo Level = "info"
LevelDebug Level = "debug"
)
type Logger interface {
// 发送可结构化的消息
// argPairs被视为键值对,键或值为nil的将不被记录
// 本地log:直接发给zap.w处理
// fluent log:将在内部拼成map {"msg": msg, arg0: arg1, arg2: arg3, ...},msg仅当参数msg非空串才会被记录
Log(level Level, msg string, argPairs ...interface{})
// 间接调用Log(LevelDebug, msg, args...),下同
Debug(msg string, args ...interface{})
Info(msg string, args ...interface{})
Warn(msg string, args ...interface{})
Error(msg string, args ...interface{})
Fatal(msg string, args ...interface{})
// 发送可结构化的消息
// 本地log:解构后发给zap.w处理
// fluent log:直接发送
LogMap(level Level, msg string, values map[string]interface{})
Debugm(msg string, values map[string]interface{})
...
// 发送简单消息
// 本地log:直接发给zap处理
// fluent log:args如仅包含一个map,会用它发给fluent,若有多个将合并为信息字符串,并将map[string]interface{}{"m":信息字符串}发给fluent
LogPlainMessage(level Level, args ...interface{})
// 发送格式化后的简单消息
// 内部逻辑与LogPlainMessage基本相同
LogFormatted(level Level, format string, args ...interface{})
// 调用LogFormatted,下同
Debugf(format string, args ...interface{})
Infof(format string, args ...interface{})
Warnf(format string, args ...interface{})
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
type MessageFormat string
const (
MessageFormatJSON MessageFormat = "json"
MessageFormatText MessageFormat = "text"
)
type TimeFormat string
const (
TimeFormatISO8601 TimeFormat = "iso8601"
TimeFormatSeconds TimeFormat = "seconds"
TimeFormatMillis TimeFormat = "millis"
TimeFormatNanos TimeFormat = "nanos"
)
type LocalFormat struct {
Format MessageFormat
MessageKey string // 默认为M
TimeKey string // 默认为T
LevelKey string // 默认为L
NameKey string // 默认为N
CallerKey string // 默认为C
// 时间格式
TimeFormat TimeFormat // 默认为TimeFormatISO8601
}
func MakeLocalFormat(msg MessageFormat) LocalFormat
type Output interface {
Write(bs []byte) error
}
type ConsoleOutput struct {
Format LocalFormat
Level Level
output *zap.SugaredLogger
}
func MakeConsoleOutput(fmt LocalFormat, level Level) ConsoleOutput
type FileOutput struct {
Format LocalFormat
Level Level
Location string
Rotation FileRotation
output *zap.SugaredLogger
}
// 结构同目前的Rotation
type FileRotation struct {...}
func MakeFileOutput(fmt LocalFormat, level Level, location string, rotation FileRotation) FileOutput
// 以app_name作为fluent的tag
type FluentOutput struct {
output *fluent.Fluent
}
type logger struct {
outpers []Output
}
func NewLogger(outpers []Output) *Loggercfg := log.NewConfig(log.LevelDebug,
log.ConsoleOutput{Format: log.FormatText},
log.FileOutput{
Format: log.FormatJSON,
Location: "output/log.txt",
Rotation: FileRotation {...},
})
cfg.ShouldLogCaller = false
cfg.TimeFormat = TimeFormatSeconds
logger := NewLogger(cfg)
logger.Debug("print something")以app_name作为prefix
package rock
func Metric() *metric.StatsdClient
// stats计数 + 1,当Application.Init初始化后可用
func MetricIncrease(key string)
// stats计数 - 1,当Application.Init初始化后可用
func MetricDecrease(key string)
// 记录时间
// duration: time.Now().Sub(oldTime)
func MetricTiming(key string, duration time.Duration)
func MetricGauge(bucket string, value interface{})
func MetricHistogram(bucket string, value interface{})
package metric
// 根据配置建立metric对象,与目前API相同
func New(opts ...Option) (*StatsdClient, error)可以用fluent插件进行中转,以避免sentry服务器承压不够影响响应速度 https://github.com/y-ken/fluent-plugin-sentry
- 在fluent服务器定义一个过滤规则,用该插件发至sentry服务器
- 定义一个包含fluent的Logger,设定符合该过滤规则的tag
- 在PanicHandler中,将信息通过该Logger发送。
package crypto
// AES
type Coder interface {
Encrypt([]byte) ([]byte, error)
Decrypt([]byte) ([]byte, error)
}
func NewAESCoderWithECB(key []byte) (Coder, error)
type aesECBCoder struct {
cipher cipher.Block
}
func NewAESCoderWithCBC(key, iv []byte) (Coder, error)
type aesCBCCoder struct {
cipher cipher.Block
iv []byte
}
// Digest
func MD5([]byte) []byte
func SHA1([]byte) []byte
func SHA256([]byte) []byte
func SHA512([]byte) []byte
// RSA
type RSAEncoder interface {
Encrypt([]byte) ([]byte, error)
VerifySign(msg, sign []byte) bool
}
type RSADecoder interface {
Decrypt([]byte) ([]byte, error)
Sign(msg []byte) ([]byte, error)
}
func NewRSAKeys(bits int) (publicKey []byte, privateKey []byte, err error)
func NewRSAEncoder(pubKey []byte) RSAEncoder
type rsaEncoder struct {
publicKey []byte
}
func NewRSADecoder(privKey []byte) RSADecoder
type rsaDecoder struct {
privateKey []byte
}import (
"git.atcloudbox.com/087-group/lib_rockgo_server/rock"
"git.atcloudbox.com/087-group/lib_rockgo_server/log"
)
func main() {
app := rock.NewApplication()
app.InitWithRockConfig("config_rock.yaml")
// 加载config目录下的yaml与json文件
rock.ImportConfigFilesFromDirectory("config")
logger := rock.Logger("Boost")
logger.Debug("app已加载配置")
// 配置中预定义的中间件已经添加(用于捕获panic、记录stats)
// 在此增加其它中间件,中间件用ctx.Next()执行后续中间件和请求处理
app.Iris().Use(func(ctx iris.Context) {
// 例: 在最初启动时记录开始访问的时间,以免后续执行过多次的time.Now()
t := time.Now()
ctx.Values().Set("now", t)
ctx.Next()
// 记录access log
rock.Logger("Access").Info("access", "remote_addr", ctx.RemoteAddr(), "status", ctx.GetStatusCode(), "now", t)
})
// 增加用于记录access log的中间件
app.Iris.Use(rock.NewAccessLogMiddleware(rock.Logger("Access")))
// 用service注册路由
app.NewService("root", "/").Get(func(ctx iris.Context) {
ctx.StatusCode(200)
})
app.NewService("user", "/user/{id:int min(1)}").
// 自动添加统计 /user/{id}.$statusCode,如/user/{id}.200或/user/{id}.400
Get(func(ctx iris.Context) {
uid := ctx.Param("id")
user, err := // get user with id
if err == nil {
ctx.StatusCode(http.StatusOK)
ctx.ResponseWriter.Write(/* marshal user */)
} else {
ctx.StatusCode(http.StatusXxx)
}
}).
Post(func(ctx iris.Context) {
// handler start
uid := ctx.Param("id")
username := ctx.Request.FormValue("name")
if username == "" {
ctx.StatusCode(http.StatusBadRequest)
return
}
// some codes in controller
logger := rock.Logger("User")
// 读取配置(或应在启动时读取)
file, _ := rock.Config("account.apns.p8_file").(string)
var err error
...
// back to handler
if err == nil {
logger.Debug("update user success", "user_id", uid)
ctx.StatusCode(http.StatusOK)
} else {
logger.Error("update user failure", "user_id", uid, "error", err)
ctx.StatusCode(http.StatusInternalServerError)
}
})
app.Iris().OnErrorCode(http.StatusNotFound, func(ctx iris.Context) {
// 路由错误处理
})
logger.Info("server start", "time", time.Now().String()})
app.Run(":8080")
}
func TestCrypto() {
var key = []byte("1234567890abcdef") // or read from KMS
plainData := []byte("Hello World")
var encryptedData []byte
println(security.NewAESCoderWithCBC(key).Encrypt(encryptedData))
rsaPublicKey, rsaPrivateKey, err := NewRSAKeys(32)
// or read keys from somewhere
encryptedData = security.NewRSAEncoder(rsaPublicKey).Encrypt(plainData)
println(security.NewRSADecoder(rsaPrivateKey).Decrypt(encryptedData))
}