Golang之mirco框架部分浅析

在实习中使用 micro 框架,但是挺多不懂的,看了部分源码搞懂了一些,还是有一些比较复杂没搞懂。

第一部分:初始化 service 并修改端口

main.go

// waitgroup is a handler wrapper which adds a handler to a sync.WaitGroup
func waitgroup(wg *util.WaitGroupWrapper) server.HandlerWrapper {
    return func(h server.HandlerFunc) server.HandlerFunc {
        return func(ctx context.Context, req server.Request, rsp interface{}) error {
            wg.Add(1)
            defer wg.Done()
            return h(ctx, req, rsp)
        }
    }
}

func main() {
    // ...
    
    var wg util.WaitGroupWrapper
    var service micro.Service
    
    // 这里在下面给出解释
    service = micro.NewService(
        // 这里返回了四个函数闭包,同时也是Option类型,NewService的参数是可变长度的Option数组
        micro.Name("xxx"),
        micro.WrapHandler(waitgroup(&wg)),
        micro.RegisterTTL(30*time.Second),
        micro.RegisterInterval(10*time.Second),
    )
    
    // 先对 service 初始化
    service.Init()
    // 因为在 go run *.go 的时候回传入参数 --server_address=":8880" 这个时候可以这样将传入的参数取出,之后给webservice使用
    addr := service.Server().Options().Address
    // 将这个的端口赋为默认地址,即:0
    service.Server().Init(server.Address(server.DefaultAddress))
    
    // ...
}

go-micro.go

type Service interface {
    Init(...Option)
    Options() Options
    Client() client.Client
    Server() server.Server
    Run() error
    String() string
}

type Option func(*Options)

// NewService creates and returns a new Service based on the packages within.
func NewService(opts ...Option) Service {
    return newService(opts...)
}

这里定义了一个 function type ,一开始对这个不太熟悉,看了 这篇文章 之后懂了点。定义这个 Option 之后的初始化操作就是将 Option 当做参数,传入到 NewXXX 中,在这些 NewXXX 方法又会遍历传入的 []Option 数组,然后调用这个 Option 在里面套一个 Init() 方法或者直接对变量进行赋值来完成对参数 *Options 的部分变量的初始化。

service.go

func newService(opts ...Option) Service {
    // newOptions 的操作在下面的 options.go 文件中
    options := newOptions(opts...)

    // 包装client,添加一些信息
    options.Client = &clientWrapper{
        options.Client,
        metadata.Metadata{
            HeaderPrefix + "From-Service": options.Server.Options().Name,
        },
    }
    
    return &service{
        opts: options,
    }
}

type service struct {
    opts Options

    once sync.Once
}

func (s *service) Init(opts ...Option) {
    // process options
    for _, o := range opts {
        o(&s.opts)
    }

    // 能保证once只执行一次,无论你是否更换once.Do(xx)这里的方法,这个sync.Once块只会执行一次。
    s.once.Do(func() {
        // save user action
        action := s.opts.Cmd.App().Action
        // 下面注释给出 cmd.App().Action 的初始化
        // func newCmd(opts ...Option) Cmd {
        //  options := Options{
        //      Broker:    &broker.DefaultBroker,
        //      Client:    &client.DefaultClient,
        //      Registry:  &registry.DefaultRegistry,
        //      Server:    &server.DefaultServer,
        //      Selector:  &selector.DefaultSelector,
        //      Transport: &transport.DefaultTransport,
        
        //      Brokers:    DefaultBrokers,
        //      Clients:    DefaultClients,
        //      Registries: DefaultRegistries,
        //      Selectors:  DefaultSelectors,
        //      Servers:    DefaultServers,
        //      Transports: DefaultTransports,
        //  }
        
        //  ...
        
        //  cmd := new(cmd)
        //  cmd.opts = options
        //  cmd.app = cli.NewApp()
        //  ...
        //  cmd.app.Action = func(c *cli.Context) {}  这里
        //  ...

        //  return cmd


        // set service action
        s.opts.Cmd.App().Action = func(c *cli.Context) {
            // set register interval
            if i := time.Duration(c.GlobalInt("register_interval")); i > 0 {
                s.opts.RegisterInterval = i * time.Second
            }

            // user action
            action(c)
        }

        // Initialise the command flags, overriding new service
        _ = s.opts.Cmd.Init(
            cmd.Broker(&s.opts.Broker),
            cmd.Registry(&s.opts.Registry),
            cmd.Transport(&s.opts.Transport),
            cmd.Client(&s.opts.Client),
            cmd.Server(&s.opts.Server),
        )
    })
}

options.go

func newOptions(opts ...Option) Options {
    opt := Options {
        Broker:    broker.DefaultBroker,
        Cmd:       cmd.DefaultCmd,
        Client:    client.DefaultClient,
        Server:    server.DefaultServer,
        Registry:  registry.DefaultRegistry,
        Transport: transport.DefaultTransport,
        Context:   context.Background(),
    }
    
    // 第一个值是下标,第二个值(o)是相当于value,这里是一个Option类型的函数。主要就是对 opt 赋值初始化
    for _, o := range opts {
        o(&opt)
    }

    return opt
}

// 下面四个函数的返回一个闭包,然后在 newOptions 的时候调用完成初始化操作
// Name of the service
func Name(n string) Option {
    return func(o *Options) {
        o.Server.Init(server.Name(n))
    }
}

// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
    return func(o *Options) {
        o.Server.Init(server.RegisterTTL(t))
    }
}

// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
    return func(o *Options) {
        o.RegisterInterval = t
    }
}

// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
    return func(o *Options) {
        var wrappers []server.Option
        
        for _, wrap := range w {
            wrappers = append(wrappers, server.WrapHandler(wrap))
        }
        
        // Init once 
        // 根据这些option初始化server
        o.Server.Init(wrappers...)
    }
}

第二部分:订阅Topic

main.go

func subEvent(ctx context.Context, stats *proto.Event) error {
    golog.Logf("Received event %+v\n", stats)
    return nil 
}

func main() {
    // ...
    // subEvent是自定义函数
    micro.RegisterSubscriber("TOPIC", service.Server(), subEvent)
    // ...
}

go-micro.go

func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...server.SubscriberOption) error {
    // 调用 server.go 的函数
    return s.Subscribe(s.NewSubscriber(topic, h, opts...))
}

server.go

var (
    DefaultServer  Server = newRpcServer()
)

func NewSubscriber(topic string, h interface{}, opts ...SubscriberOption) Subscriber {
    return DefaultServer.NewSubscriber(topic, h, opts...)
}

func Subscribe(s Subscriber) error {
    // 这里的 DefaultServer 是 RpcServer,调用下面 rpc_server.go 的函数
    return DefaultServer.Subscribe(s)
}

rpc_server.go

// 调用 subscriber.go 中的函数
func (s *rpcServer) NewSubscriber(topic string, sb interface{}, opts ...SubscriberOption) Subscriber {
    return newSubscriber(topic, sb, opts...)
}

subscriber.go

type handler struct {
    method  reflect.Value
    reqType reflect.Type
    ctxType reflect.Type
}

type subscriber struct {
    topic      string
    rcvr       reflect.Value
    typ        reflect.Type
    subscriber interface{}
    handlers   []*handler
    endpoints  []*registry.Endpoint
    opts       SubscriberOptions
}

func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
    // 利用传进来的 SubcriberOption 初始化,这个和第一部分的类似,略过
    var options SubscriberOptions
    for _, o := range opts {
        o(&options)
    }

    var endpoints []*registry.Endpoint
    var handlers []*handler
    
    // 利用反射去对传进来的 interface{} 操作,我在前面最开始传的是 subEvent 函数
    if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
        // 因为我传入的是 Func 类型,所以会进入这里
        h := &handler{
            // 获取方法
            method: reflect.ValueOf(sub),
        }
        
        // 判断参数个数,并分别给对应位置的类型赋值
        switch typ.NumIn() {
        case 1:
            h.reqType = typ.In(0)
        case 2:
            h.ctxType = typ.In(0)
            h.reqType = typ.In(1)
        }

        handlers = append(handlers, h)

        endpoints = append(endpoints, &registry.Endpoint{
            Name:    "Func",
            Request: extractSubValue(typ),
            Metadata: map[string]string{
                "topic":      topic,
                "subscriber": "true",
            },
        })
    } else {
        hdlr := reflect.ValueOf(sub)
        name := reflect.Indirect(hdlr).Type().Name()

        for m := 0; m < typ.NumMethod(); m++ {
            method := typ.Method(m)
            h := &handler{
                method: method.Func,
            }

            switch method.Type.NumIn() {
            case 2:
                h.reqType = method.Type.In(1)
            case 3:
                h.ctxType = method.Type.In(1)
                h.reqType = method.Type.In(2)
            }

            handlers = append(handlers, h)

            endpoints = append(endpoints, &registry.Endpoint{
                Name:    name + "." + method.Name,
                Request: extractSubValue(method.Type),
                Metadata: map[string]string{
                    "topic":      topic,
                    "subscriber": "true",
                },
            })
        }
    }

    return &subscriber{
        rcvr:       reflect.ValueOf(sub),
        typ:        reflect.TypeOf(sub),
        topic:      topic,
        subscriber: sub,
        handlers:   handlers,
        endpoints:  endpoints,
        opts:       options,
    }
}

rpc_server.go

func (s *rpcServer) Subscribe(sb Subscriber) error {
    sub, ok := sb.(*subscriber)
    if !ok {
        return fmt.Errorf("invalid subscriber: expected *subscriber")
    }
    if len(sub.handlers) == 0 {
        return fmt.Errorf("invalid subscriber: no handler functions")
    }
    
    // 验证是否合法
    if err := validateSubscriber(sb); err != nil {
        return err
    }

    s.Lock()
    defer s.Unlock()
    _, ok = s.subscribers[sub]
    if ok {
        return fmt.Errorf("subscriber %v already exists", s)
    }
    // 置为nil,nil也是值
    s.subscribers[sub] = nil
    return nil
}

第三部分:运行 service

main.go

// 启动订阅服务
if err := service.Run(); err != nil {
    log.Fatal(err)
}

调用 service.goRun() 方法。

service.go

func (s *service) Run() error {
    if err := s.Start(); err != nil {
        return err
    }

    // start reg loop
    ex := make(chan bool)
    
    // 看下面
    go s.run(ex)

    ch := make(chan os.Signal, 1)
    signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)

    select {
    // wait on kill signal
    case <-ch:
    // wait on context cancel
    case <-s.opts.Context.Done():
    }

    // exit reg loop
    close(ex)

    return s.Stop()
}

func (s *service) Start() error {
    // 遍历在开始前的需要执行的函数并执行
    for _, fn := range s.opts.BeforeStart {
        if err := fn(); err != nil {
            return err
        }
    }

    // 调用 rpc_server 的 Start()
    if err := s.opts.Server.Start(); err != nil {
        return err
    }
    
    // 调用 rpc_server 的 Register()
    if err := s.opts.Server.Register(); err != nil {
        return err
    }

    // 遍历在开始后的需要执行的函数并执行
    for _, fn := range s.opts.AfterStart {
        if err := fn(); err != nil {
            return err
        }
    }

    return nil
}

func (s *service) run(exit chan bool) {
    if s.opts.RegisterInterval <= time.Duration(0) {
        return
    }

    t := time.NewTicker(s.opts.RegisterInterval)

    for {
        select {
        case <-t.C:
            err := s.opts.Server.Register()
            if err != nil {
                log.Log("service run Server.Register error: ", err)
            }
        case <-exit:
            t.Stop()
            return
        }
    }
}

func (s *service) Stop() error {
    var gerr error

    for _, fn := range s.opts.BeforeStop {
        if err := fn(); err != nil {
            gerr = err
        }
    }
    
    // 取消注册
    if err := s.opts.Server.Deregister(); err != nil {
        return err
    }
    
    // 停止
    if err := s.opts.Server.Stop(); err != nil {
        return err
    }

    for _, fn := range s.opts.AfterStop {
        if err := fn(); err != nil {
            gerr = err
        }
    }

    return gerr
}

Start() 方法会调用 server.goStart() 方法

server.go

func (s *rpcServer) Start() error {
    registerDebugHandler(s)
    config := s.Options()

    // 返回一个监听该地址的 listener
    ts, err := config.Transport.Listen(config.Address)
    if err != nil {
        return err
    }

    log.Logf("Listening on %s", ts.Addr())
    s.Lock()
    s.opts.Address = ts.Addr()
    s.Unlock()

    // 启动一条协程接收信息,内部调用了 net 包的 Accept,看下面的 accept()
    go ts.Accept(s.accept)

    go func() {
        // wait for exit
        ch := <-s.exit

        // wait for requests to finish
        if wait(s.opts.Context) {
            s.wg.Wait()
        }

        // close transport listener
        ch <- ts.Close()

        // disconnect the broker
        config.Broker.Disconnect()
    }()

    // TODO: subscribe to cruft
    return config.Broker.Connect()
}

func (s *rpcServer) accept(sock transport.Socket) {
    defer func() {
        // close socket
        sock.Close()

        if r := recover(); r != nil {
            log.Log("panic recovered: ", r)
            log.Log(string(debug.Stack()))
        }
    }()

    for {
        var msg transport.Message
        
        // 接收信息存放到msg中
        if err := sock.Recv(&msg); err != nil {
            return
        }

        // we use this Timeout header to set a server deadline
        to := msg.Header["Timeout"]
        // we use this Content-Type header to identify the codec needed
        ct := msg.Header["Content-Type"]

        // 将原来的 Content-Type 转换为可以被 rpc 使用的 Type
        cf, err := s.newCodec(ct)
        // TODO: needs better error handling
        if err != nil {
            sock.Send(&transport.Message{
                Header: map[string]string{
                    "Content-Type": "text/plain",
                },
                Body: []byte(err.Error()),
            })
            return
        }

        // 返回一个新的 codec
        codec := newRpcPlusCodec(&msg, sock, cf)

        // strip our headers
        hdr := make(map[string]string)
        for k, v := range msg.Header {
            hdr[k] = v
        }
        delete(hdr, "Content-Type")
        delete(hdr, "Timeout")

        ctx := metadata.NewContext(context.Background(), hdr)

        // set the timeout if we have it
        if len(to) > 0 {
            if n, err := strconv.ParseUint(to, 10, 64); err == nil {
                ctx, _ = context.WithTimeout(ctx, time.Duration(n))
            }
        }

        // add to wait group
        s.wg.Add(1)
        defer s.wg.Done()

        // TODO: needs better error handling
        // 里面包括了许多方法:
        // server.readRequest() // 从codec中读取请求
        // server.sendResponse() // 最后会将response的信息通过socket发送
        // service.call()
        if err := s.rpc.serveRequest(ctx, codec, ct); err != nil {
            log.Logf("Unexpected error serving request, closing socket: %v", err)
            return
        }
    }
}


func (s *rpcServer) Register() error {
    // parse address for host, port
    config := s.Options()
    var advt, host string
    var port int

    // check the advertise address first
    // if it exists then use it, otherwise
    // use the address
    if len(config.Advertise) > 0 {
        advt = config.Advertise
    } else {
        advt = config.Address
    }

    parts := strings.Split(advt, ":")
    if len(parts) > 1 {
        host = strings.Join(parts[:len(parts)-1], ":")
        port, _ = strconv.Atoi(parts[len(parts)-1])
    } else {
        host = parts[0]
    }

    addr, err := addr.Extract(host)
    if err != nil {
        return err
    }

    // register service
    node := &registry.Node{
        Id:       config.Name + "-" + config.Id,
        Address:  addr,
        Port:     port,
        Metadata: config.Metadata,
    }

    node.Metadata["transport"] = config.Transport.String()
    node.Metadata["broker"] = config.Broker.String()
    node.Metadata["server"] = s.String()
    node.Metadata["registry"] = config.Registry.String()

    s.RLock()
    // Maps are ordered randomly, sort the keys for consistency
    // 生成 handlerList
    var handlerList []string
    for n, e := range s.handlers {
        // Only advertise non internal handlers
        if !e.Options().Internal {
            handlerList = append(handlerList, n)
        }
    }
    sort.Strings(handlerList)

    // 生成 subscriberList
    var subscriberList []*subscriber
    for e := range s.subscribers {
        // Only advertise non internal subscribers
        if !e.Options().Internal {
            subscriberList = append(subscriberList, e)
        }
    }
    sort.Slice(subscriberList, func(i, j int) bool {
        return subscriberList[i].topic > subscriberList[j].topic
    })

    var endpoints []*registry.Endpoint
    for _, n := range handlerList {
        endpoints = append(endpoints, s.handlers[n].Endpoints()...)
    }
    for _, e := range subscriberList {
        endpoints = append(endpoints, e.Endpoints()...)
    }
    s.RUnlock()

    service := &registry.Service{
        Name:      config.Name,
        Version:   config.Version,
        Nodes:     []*registry.Node{node},
        Endpoints: endpoints,
    }

    s.Lock()
    registered := s.registered
    s.Unlock()

    if !registered {
        log.Logf("Registering node: %s", node.Id)
    }

    // create registry options
    rOpts := []registry.RegisterOption{registry.RegisterTTL(config.RegisterTTL)}

    if err := config.Registry.Register(service, rOpts...); err != nil {
        return err
    }

    // already registered? don't need to register subscribers
    if registered {
        return nil
    }

    s.Lock()
    defer s.Unlock()

    s.registered = true

    for sb, _ := range s.subscribers {
        handler := s.createSubHandler(sb, s.opts)
        var opts []broker.SubscribeOption
        if queue := sb.Options().Queue; len(queue) > 0 {
            opts = append(opts, broker.Queue(queue))
        }
        // 订阅
        sub, err := config.Broker.Subscribe(sb.Topic(), handler, opts...)
        if err != nil {
            return err
        }
        // 放入之前设为 nil 的 map 里面
        s.subscribers[sb] = []broker.Subscriber{sub}
    }

    return nil
}

第四部分:初始化WebService

main.go

// 对主页服务
// 自定义的Handler函数 serveHome
func serveHome(w http.ResponseWriter, r *http.Request) {
    log.Println(r.URL)
    if r.URL.Path != "/" {
        http.Error(w, "Not found", http.StatusNotFound)
        return
    }
    log.Println(r.URL.Path[1:])
    http.ServeFile(w, r, "./view/login.html")
}

func main() {
    // ...
    // 初始化操作和前面类似,略过
    webService := web.NewService(
        web.Name("xxx"),
        web.RegisterTTL(30*time.Second),
        web.RegisterInterval(10*time.Second),
        web.Address(addr),
        web.Registry(service.Options().Registry),
    )
    // 部署静态资源,看下面
    // http.FileServer()返回一个Handler,将 "" 目录(即当前目录)下的资源部署
    webService.Handle("/view/", http.FileServer(http.Dir("")))
    
    // 主页
    // 自定义的Handler函数 serveHome
    webService.HandleFunc("/", serveHome)
    
    if err := webService.Run(); err != nil {
        log.Fatal(err)
    }
    // ...
}

service.go

func (s *service) Handle(pattern string, handler http.Handler) {
    var seen bool
    for _, ep := range s.srv.Endpoints {
        if ep.Name == pattern {
            seen = true
            break
        }
    }
    if !seen {
        s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
            Name: pattern,
        })
    }
    // 底层调用http包的方法
    s.mux.Handle(pattern, handler)
}

func (s *service) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
    var seen bool
    for _, ep := range s.srv.Endpoints {
        if ep.Name == pattern {
            seen = true
            break
        }
    }
    if !seen {
        s.srv.Endpoints = append(s.srv.Endpoints, &registry.Endpoint{
            Name: pattern,
        })
    }

    s.mux.HandleFunc(pattern, handler)
}

net/http/server.go

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// HandlerFunc 实现了 ServeHTTP 方法,因此实现了Handler接口
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()

    if pattern == "" {
        panic("http: invalid pattern")
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if _, exist := mux.m[pattern]; exist {
        panic("http: multiple registrations for " + pattern)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }
}

附无用Demo

sync.Once

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    for i := 0; i < 10; i++ {
        once.Do(func() {
            fmt.Println("once :", i)
        })
    }
}

输出:
once : 0

type function

package main

import (
    "fmt"
)

type Fun func(s string)

func A(s string) {
    fmt.Println("a : ", s)
}

func B(s string) {
    fmt.Println("b : ", s)
}

func C(s string) {
    fmt.Println("c : ", s)
}

func D(s string) {
    fmt.Println("d : ", s)
}

func main() {
    f := []Fun{A, B, C, D}
    for k, v := range f {
        fmt.Println(k)
        v("233")
    }
}

输出:
0
a :  233
1
b :  233
2
c :  233
3
d :  233

转载于:https://www.cnblogs.com/fightfordream/p/9083859.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值