1 Star 0 Fork 0

天雨流芳 / go-micro-framework

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
app.go 5.31 KB
一键复制 编辑 原始数据 按行查看 历史
天雨流芳 提交于 2024-03-29 13:57 . 修改server启动和停止逻辑
package app
import (
"context"
"errors"
log "gitee.com/tylf2018/go-micro-framework/pkg/logger"
"gitee.com/tylf2018/go-micro-framework/registry"
"gitee.com/tylf2018/go-micro-framework/server"
"golang.org/x/sync/errgroup"
"os"
"os/signal"
"sync"
)
type App struct {
// struct 类型 说明是实例化后的 可以直接用 不需要再次声明
lk sync.Mutex
opts *Options
cancel func()
instance *registry.ServiceInstance // registry 参数
}
func New(opts ...Option) *App {
o := DefaultOptions()
if len(opts) > 0 {
// 执行附加参数
for _, opt := range opts {
opt(o)
}
}
return &App{
opts: o,
}
}
// Run 启动整个微服务
func (a *App) Run() error {
// 生成注册的信息,一开始就生成,后面有注册对象的时候可以直接使用
instance, err := a.buildServerInstance()
if err != nil {
return err
}
// 这个变量可能被其他的 goroutine 访问到
a.lk.Lock()
a.instance = instance
a.lk.Unlock()
// 启动 restServer 和 rpcServer
eg, startCtx := a.startServers()
// 注册服务到注册中心
if a.opts.registrar != nil {
ctx, cancelFunc := context.WithTimeout(context.Background(), a.opts.registrarTimeout)
defer cancelFunc()
err := a.opts.registrar.Register(ctx, a.instance)
if err != nil {
log.ErrorF("register service error: %s", err)
return err
}
}
// 监听退出信号
// 监听退出信号
c := make(chan os.Signal)
signal.Notify(c, a.opts.sigs...)
eg.Go(func() error {
select {
case <-startCtx.Done(): // 主动关闭
return startCtx.Err()
case <-c: // 获取退出信号
return a.Stop() // 执行退出
}
})
if err := eg.Wait(); err != nil {
return err
}
return nil
}
// Stop 停止服务
func (a *App) Stop() error {
a.lk.Lock()
instance := a.instance
a.lk.Unlock()
if a.opts.registrar != nil && instance != nil {
// 这里需要强制退出,避免收到退出命令一直退出不了
ctx, cancelFunc := context.WithTimeout(context.Background(), a.opts.registrarTimeout)
defer cancelFunc()
err := a.opts.registrar.Deregister(ctx, instance)
if err != nil {
log.ErrorF("deregister service error: %s", err)
return err
}
}
// 给server发送退出消息
if a.cancel != nil {
a.cancel()
}
return nil
}
func (a *App) startServers() (*errgroup.Group, context.Context) {
//解决多个server的同时启动情况的问题分析
/*
现在启动了两个server,一个是restserver,一个是rpcserver
这两个server是否必须同时启动成功?
如果有一个启动失败,那么我们就要停止另外一个server
如果启动了多个, 如果其中一个启动失败,其他的应该被取消
如果剩余的server的状态:
1. 还没有开始调用start
stop
2. start进行中
调用进行中的cancel
3. start已经完成
调用stop
如果服务启动了然后这个时候用户立马进行了访问
*/
// 生成退出是调用的cancel方法
canCtx, cancelFunc := context.WithCancel(context.Background())
a.cancel = cancelFunc
// 使用errgroup包可以解决这一问题:
eg, ctx := errgroup.WithContext(canCtx)
// 服务的协程启动后会进行阻塞,会导致后面的代码也没法执行,所以需要一个wg来判断是否已经启动
wg := sync.WaitGroup{}
for _, srv := range a.opts.servers {
// 用一个临时变量,这样 协程中调用的 srv 就会引用函数内部的变量 不会因为 srv 的改变而改变
// 不做此操作 有可能发生 下面协程 srv.Start 中 srv 启动的是 其他的 微服务 而没启动 本身的微服务
s := srv
// 先启动一个 groutine 去监听是否有 err 产生,如果某个协程产生了error,就将其对应的server停止
eg.Go(func() error {
<-ctx.Done() // wait for stop signal
// 接收到 关闭信号后 先设置一个拥有超时功能的 ctx
// 不可能无休止的等待 srv.Stop 执行完成, 如果超时 强制退出 (需要内部 Stop 写好相关逻辑)
sctx, cancel := context.WithTimeout(context.Background(), a.opts.stopTimeout)
defer cancel()
return srv.Stop(sctx)
})
wg.Add(1)
eg.Go(func() error {
// 这里出现错误,就会通知同一个parentCtx下面的所有协程,也就是说所有的服务都会收到退出的指令
log.Info("start rest server")
wg.Done()
sctx, cancel := context.WithTimeout(context.Background(), a.opts.startTimeout)
defer cancel()
return s.Start(sctx)
})
}
// 上面 api 和 grpc 服务都正常开启后 才能继续运行 否则会 hold 住
wg.Wait()
return eg, ctx
}
// 创建服务注册结构体
func (a *App) buildServerInstance() (*registry.ServiceInstance, error) {
endpoints := make([]string, 0, len(a.opts.endpoints))
if len(a.opts.endpoints) > 0 {
for _, e := range a.opts.endpoints {
endpoints = append(endpoints, e.String())
}
}
// 如果endpoints为0,尝试从服务中获取
if len(endpoints) == 0 {
for _, srv := range a.opts.servers {
if r, ok := srv.(server.Endpointer); ok {
e, err := r.Endpoint()
if err != nil {
return nil, err
}
endpoints = append(endpoints, e.String())
}
}
}
if len(endpoints) == 0 {
return nil, errors.New("registry endpoints is empty")
}
return &registry.ServiceInstance{
ID: a.opts.id,
Name: a.opts.name,
Endpoints: endpoints,
Tags: a.opts.tags,
}, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/tylf2018/go-micro-framework.git
git@gitee.com:tylf2018/go-micro-framework.git
tylf2018
go-micro-framework
go-micro-framework
4cc90ded505a

搜索帮助

344bd9b3 5694891 D2dac590 5694891