更新时间:2022-08-12 19:40:41
date: 2020-08-11 23:52:19
title: learn| jupiter 学习笔记一
生命不息, 学习不止, 这次我们来折腾 jupiter 框架
【斗鱼】没人比我更懂微服务-Go 微服务框架Jupiter
把官网的 example 都实现了一遍, 才发现 helloworld 应该是这样的:
package main
import (
"github.com/douyu/jupiter"
"github.com/douyu/jupiter/pkg/xlog"
)
func main() {
var app jupiter.Application
app.Startup() // 启动框架, 可以使用框架的各种功能了
xlog.Info("hello world")
}
package main
import (
"github.com/douyu/jupiter"
"github.com/douyu/jupiter/pkg/xlog"
)
func main() {
var app jupiter.Application
app.Startup(testLog) // 支持在框架初始化后, 执行特定的方法
}
func testLog() error { // 封装成方法
xlog.Info("hello world")
return nil
}
package main
import (
"fmt"
"github.com/douyu/jupiter"
"github.com/douyu/jupiter/pkg/xlog"
)
func main() {
eng := NewEngine()
fmt.Println(eng)
}
type Engine struct {
jupiter.Application
}
func NewEngine() *Engine {
eng := &Engine{}
eng.Startup(testLog)
return eng
}
func testLog() error {
xlog.Info("hello world")
return nil
}
PS: 为了下面讲解代码方便, 均不使用套壳版
Startup
干了些啥func (app *Application) Startup(fns ...func() error) error {
app.initialize() // 初始化 app
if err := app.startup(); err != nil { // 初始化 falg/log/config/trace/governor 等模块
return err
}
return xgo.SerialUntilError(fns...)() // 这是为啥支持传入多个方法
}
package main
import (
"github.com/douyu/jupiter"
"github.com/douyu/jupiter/pkg/conf" // conf 模块
"github.com/douyu/jupiter/pkg/registry/compound"
"github.com/douyu/jupiter/pkg/registry/etcdv3" // 除了 registry, 还是 client 的使用例子
"github.com/douyu/jupiter/pkg/server"
"github.com/douyu/jupiter/pkg/server/xecho"
"github.com/douyu/jupiter/pkg/server/xgin"
"github.com/douyu/jupiter/pkg/worker"
"github.com/douyu/jupiter/pkg/worker/xcron"
"github.com/douyu/jupiter/pkg/xlog" // log 模块
"github.com/gin-gonic/gin"
"github.com/labstack/echo/v4"
"time"
)
func main() {
var app jupiter.Application
// 初始化框架的功能, 这里额外传入了
app.Startup(fileWatcher)
// 修改 xlog.DefaultLogger, 从而改变 xlog 的行为
// 后面会具体讲解 config/log 模块
xlog.DefaultLogger = xlog.StdConfig("default").Build()
// 可以启动多个 server
app.Serve(startEcho())
app.Serve(startGin())
// 可以设置注册中心, server 启动是会自动注册进去, 这里使用 etcd 作为注册中心
app.SetRegistry(compound.New(etcdv3.StdConfig("etcd").Build()))
// 设置 worker
app.Schedule(startWorker())
// 启动应用
app.Run()
}
func fileWatcher() error {
go func() {
peopleName := conf.GetString("people.name")
xlog.Info(peopleName)
time.Sleep(time.Second*10)
}()
return nil
}
func startEcho() server.Server {
s := xecho.DefaultConfig().Build()
s.GET("/hello", func(c echo.Context) error {
return c.JSON(200, "echo")
})
return s
}
func startGin() server.Server {
s := xgin.StdConfig("http").Build()
s.GET("/gin", func(c *gin.Context) {
c.JSON(200, "hello")
})
return s
}
func startWorker() worker.Worker {
cron := xcron.DefaultConfig().Build()
cron.Schedule(xcron.Every(time.Second*10), xcron.FuncJob(func() error {
xlog.Info("cron")
return nil
}))
return cron
}
# jupiter 默认提供, governor 用于服务治理
[jupiter.server.governor]
enable = false
port = 2345
# server 配置
# http server: echo gin goframe
# grpc server
[jupiter.server.http]
#enable = false
port = 1234
# registry: registry + 具体实现(这里是 etcd)
[jupiter.registry.etcd]
configKey = "jupiter.etcdv3.default"
timeout = "1s"
[jupiter.etcdv3.default]
endpoints = ["127.0.0.1:2379"]
secure = false
[jupiter.cron.test]
withSeconds = false
concurrentDelay= -1
immediatelyRun = false
[jupiter.logger.default]
debug = true
enableConsole = true
async = false
# 自定义配置
[people]
name = "daydaygo"
app.Startup(fileWatcher)
: 上一步讲到, 初始化框架的功能, 这里传入了 fileWatcher
, 可以使用动态更新配置, 后面会详细讲 -watch
功能app.Serve()
: 设置 serverapp.Schedule()
: 设置 workerapp.run()
: 启动 app, 执行 server/worker 等内容app.run()
源码就明白了func (app *Application) Run(servers ...server.Server) error {
app.smu.Lock()
app.servers = append(app.servers, servers...) // app.Serve() 其实就是设置 app.servers 变量
app.smu.Unlock()
app.waitSignals() //start signal listen task in goroutine
defer app.clean()
// todo jobs not graceful
app.startJobs()
// start servers and govern server
app.cycle.Run(app.startServers) // 这里完成 server + server 注册到注册中心
// start workers
app.cycle.Run(app.startWorkers) // 这里执行 worker
//blocking and wait quit
if err := <-app.cycle.Wait(); err != nil {
app.logger.Error("jupiter shutdown with error", xlog.FieldMod(ecode.ModApp), xlog.FieldErr(err))
return err
}
app.logger.Info("shutdown jupiter, bye!", xlog.FieldMod(ecode.ModApp))
return nil
}
默认配置文件使用 toml 格式, 使用 --config
flag 来使用本地配置文件
go run main.go --conifg=config.toml
属于 jupiter 的模块, 使用 [jupiter.模块名.名字]
来使用, 比如 [jupiter.server.http]
, 则是一个 jupiter server 的配置, 这个 server 名字为 http
jupiter 中通过 2 类配置来初始化模块:
// 使用默认配置
xlog.DefaultConfig().Build()
// 使用配置文件: [jupiter.logger.default]
xlog.xlog.StdConfig("default").Build()
理解了上面这些, 就掌握了配置的核心用法, 使用 Apollo/etcd 等配置中心, 配置文件的 filewatch 都是在此基础之上
上面其实已经看到 log 的模块的用法了, 需要修改 log 的行为, 只需要修改配置, 并且使用如下代码设置生效即可:
// 设置 DefaultLogger 即可
xlog.DefaultLogger = xlog.StdConfig("default").Build()
// 看一下 xlog.info 的源码就能知道答案
func Info(msg string, fields ...Field) {
DefaultLogger.Info(msg, fields...)
}
只要理解了这一点, 就已经理解了日志的核心用法, 日志 level, 日志输出到 stdout/file 都在此基础之上
server 这部分内容是 jupiter 的重中之中, jupiter 增加了对 echo/gin/frame/grpc 等 server 的适配使用 xecho/xgin/xframe/xgrpc 等进行配置和使用, 非常的简洁方便
使用 registry 适配配置中心, 目前适配了 etcd
使用 governor 进行服务治理(在 app.startuUp
阶段就设置好了, 在 app.run
阶段启动)
理解了这几个模块之间的关系, 就很容易理解 server 模块的核心用法
worker 比较简单, 对应 [jupiter.cron.xxx]
下的配置, 按需设置即可
go run main.go -h
查看-watch
的场景:
自己遇到的一些问题
async
, 在 server 启动后, 每隔 30s 输出一次, 这导致我通过 log 来验证的场景, 以为是遇到 bug 了context timeout
触发了jupiter 很多功能都需要 etcd 支持, 可以使用 docker-compose, 本地快速起起来:
version: '3'
services:
etcd:
image: quay.io/coreos/etcd
environment:
ETCD_ADVERTISE_CLIENT_URLS: "http://0.0.0.0:2379"
ETCD_LISTEN_CLIENT_URLS: "http://0.0.0.0:2379"
ETCDCTL_API: "3"
ports:
- 12379:2379 # http, 本地的端口自己设置
# - 2380:2380 # 节点间
# - 4001:4001
etcda: # 简单的管理界面
image: evildecay/etcdkeeper
environment:
HOST: 0.0.0.0
ports:
- 10280:8080 # 本地的端口自己设置
links:
- etcd
也可以直接使用 etcdctl 来测试:
# install
brew install etcd
# use
etcdctl --endpoints=127.0.0.1:12379 get '/hello'
jupiter 的这次开源在我这个开源老兵(github star 4k+ 和 star 3k+ 框架的核心开发者)看来看来确实有些仓促, 主要集中在文档这块, 至于源码, 目前 实力不允许, 总得多看看多写写, 能拿出足够多的干货时再 BB
从目前文档看到的几个问题:
edit on github
一直 404, 我已经给开发组提了 PR
关于文档中错误的部分, 我也一并提交了一个 PR
最后来几句开源老兵的叨逼叨: