zoukankan      html  css  js  c++  java
  • go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])

    go微服务框架kratos学习笔记五(kratos 配置中心 paladin config sdk [断剑重铸之日,骑士归来之时])


    本节看看kratos的配置中心paladin(骑士)。
    kratos对配置文件进行了梳理,配置管理模块化,如redis有redis的单独配置文件、bm有bm的单独配置文件,及为方便易用。

    paladin 本质是一个config SDK客户端,包括了remote、file、mock几个抽象功能,方便使用本地文件或者远程配置中心,并且集成了对象自动reload功能。

    现在看看paladin的几种配置方式 :

    静态配置

    照常 new 一个demo项目.

    kratos new paladin
    

    随便找个配置,看目录结构都知道http.toml在configs下,可以直接用名字get到,应该是kratos工具做了封装。

    http.toml

    [Server]
        addr = "0.0.0.0:8000"
        timeout = "1s"
    
    // New new a bm server.
    func New(s pb.DemoServer) (engine *bm.Engine, err error) {
    	var (
    		cfg bm.ServerConfig
    		ct paladin.TOML
    	)
    	if err = paladin.Get("http.toml").Unmarshal(&ct); err != nil {
    		return
    	}
    	if err = ct.Get("Server").UnmarshalTOML(&cfg); err != nil {
    		return
    	}
    

    Get() 取到的是个Value结构,利用了encoding包(encoding包定义了供其它包使用的可以将数据在字节水平和文本表示之间转换的接口)做抽象接口。

    // Value is config value, maybe a json/toml/ini/string file.
    type Value struct {
    	val   interface{}
    	slice interface{}
    	raw   string
    }
    
    // Unmarshal is the interface implemented by an object that can unmarshal a textual representation of itself.
    func (v *Value) Unmarshal(un encoding.TextUnmarshaler) error {
    	text, err := v.Raw()
    	if err != nil {
    		return err
    	}
    	return un.UnmarshalText([]byte(text))
    }
    
    // UnmarshalTOML unmarhsal toml to struct.
    func (v *Value) UnmarshalTOML(dst interface{}) error {
    	text, err := v.Raw()
    	if err != nil {
    		return err
    	}
    	return toml.Unmarshal([]byte(text), dst)
    }
    
    

    直接kratos run的话,默认是读取的configs下的本地文件。 kratos/tool/run.go 里面是可以找到的.

    package main
    
    import (
    	"os"
    	"os/exec"
    	"path"
    	"path/filepath"
    
    	"github.com/urfave/cli"
    )
    
    func runAction(c *cli.Context) error {
    	base, err := os.Getwd()
    	if err != nil {
    		panic(err)
    	}
    	dir := buildDir(base, "cmd", 5)
    	conf := path.Join(filepath.Dir(dir), "configs")
    	args := append([]string{"run", "main.go", "-conf", conf}, c.Args()...)
    	cmd := exec.Command("go", args...)
    	cmd.Dir = dir
    	cmd.Stdout = os.Stdout
    	cmd.Stderr = os.Stderr
    	if err := cmd.Run(); err != nil {
    		panic(err)
    	}
    	return nil
    }
    

    flag注入

    如果我们进行了build

    I:VSProjectkratos-notepaladinpaladin>cd cmd
    
    I:VSProjectkratos-notepaladinpaladincmd>kratos build
    directory: I:VSProjectkratos-notepaladinpaladin/cmd
    kratos: 0.3.1
    build success.
    
    I:VSProjectkratos-notepaladinpaladincmd>cmd.exe
    INFO 12/30-22:25:07.054 I:/VSProject/kratos-note/paladin/paladin/cmd/main.go:19 paladin start
    panic: lack of remote config center args
    
    goroutine 1 [running]:
    github.com/bilibili/kratos/pkg/conf/paladin.Init(0x0, 0x0, 0x0, 0x0, 0x0)
            I:/VSProject/go/pkg/mod/github.com/bilibili/kratos@v0.3.2-0.20191224125553-6e1180f53a8e/pkg/conf/paladin/default.go:32 +0x25f
    main.main()
            I:/VSProject/kratos-note/paladin/paladin/cmd/main.go:20 +0x103
    
    I:VSProjectkratos-notepaladinpaladincmd>
    

    会发现直接运行时跑不起来的,因为这时候找不到配置文件,因为这时候我们没有调用kratos run,paladin找不到配置目录。

    实际paladin里面会有一个confPath变量,主函数做paladin.init()的时候会做flag注入。也方便了开发环境开发人员自行做配置修改。

    package paladin
    
    import (
    	"context"
    	"errors"
    	"flag"
    )
    
    var (
    	// DefaultClient default client.
    	DefaultClient Client
    	confPath      string
    )
    
    func init() {
    	flag.StringVar(&confPath, "conf", "", "default config path")
    }
    
    // Init init config client.
    // If confPath is set, it inits file client by default
    // Otherwise we could pass args to init remote client
    // args[0]: driver name, string type
    func Init(args ...interface{}) (err error) {
    	if confPath != "" {
    		DefaultClient, err = NewFile(confPath)
    	} else {
    		var (
    			driver Driver
    		)
    		
       ......
    
    I:VSProjectkratos-notepaladinpaladincmd>cmd.exe -conf=I:VSProjectkratos-notepaladinpaladinconfigs
    INFO 12/30-22:41:43.717 I:/VSProject/kratos-note/paladin/paladin/cmd/main.go:19 paladin start
    2019/12/30 22:41:43 start watch filepath: I:VSProjectkratos-notepaladinpaladinconfigs
    INFO 12/30-22:41:43.781 I:/VSProject/go/pkg/mod/github.com/bilibili/kratos@v0.3.2-0.20191224125553-6e1180f53a8e/pkg/net/http/blademaster/server.go:98 blademaster: start http listen addr: 0.0.0.0:8000
    [warden] config is Deprecated, argument will be ignored. please use -grpc flag or GRPC env to configure warden server.
    INFO 12/30-22:41:43.790 I:/VSProject/go/pkg/mod/github.com/bilibili/kratos@v0.3.2-0.20191224125553-6e1180f53a8e/pkg/net/rpc/warden/server.go:329 warden: start grpc listen addr: [::]:9000
    

    在线热加载配置

    在线读取、变更的配置信息,比如某个业务开关,实现配置reload实时更新。

    // Map is config map, key(filename) -> value(file).
    type Map struct {
    	values atomic.Value
    }
    

    paladin.Map 通过 atomic.Value 自动热加载

    # service.go
    type Service struct {
    	ac *paladin.Map
    }
    
    func New() *Service {
    	// paladin.Map 通过atomic.Value支持自动热加载
    	var ac = new(paladin.TOML)
    	if err := paladin.Watch("application.toml", ac); err != nil {
    		panic(err)
    	}
    	s := &Service{
    		ac: ac,
    	}
    	return s
    }
    
    func (s *Service) Test() {
    	sw, err := s.ac.Get("switch").Bool()
    	if err != nil {
    		// TODO
    	}
    	
    	// or use default value
    	sw := paladin.Bool(s.ac.Get("switch"), false)
    }
    

    测试

    测试一下自动热加载,可以看到demo里面已经有添加了application.toml的自动热加载了

     if err := paladin.Watch("application.toml", ac); err != nil {
      panic(err)
     }
    
    

    关于watch

    可以看到watch里面有个协程在监视变动情况。

    
    // Watch watch on a key. The configuration implements the setter interface, which is invoked when the configuration changes.
    func Watch(key string, s Setter) error {
    	v := DefaultClient.Get(key)
    	str, err := v.Raw()
    	if err != nil {
    		return err
    	}
    	if err := s.Set(str); err != nil {
    		return err
    	}
    	go func() {
    		for event := range WatchEvent(context.Background(), key) {
    			s.Set(event.Value)
    		}
    	}()
    	return nil
    }
    

    我们写个get()接口,打印下app.toml的keys() 看看是否会热加载。

    func (s *Service) Get(ctx context.Context, req *pb.Req) (reply *pb.Resp, err error) {
    	
    	log.Info("app toml : (%v)", s.ac.Keys())
    

    app.toml

    # This is a TOML document. Boom~
    demoExpire = "24h"
    
    [app]
    addr = ["127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"]
    

    接着随便加个字段,再次调用get()可以看ttt到加了进来

    # This is a TOML document. Boom~
    demoExpire = "24h"
    
    [app]
    addr = ["127.0.0.1:9001", "127.0.0.1:9002", "127.0.0.1:9003"]
    
    [ttt]
    xixi = "haha"
    

    远程配置中心

    通过环境变量注入,例如:APP_ID/DEPLOY_ENV/ZONE/HOSTNAME,然后通过paladin实现远程配置中心SDK进行配合使用。

    目前只可以看到这个步骤是在Init()的时候做的,paladin本质是个客户端包,在不知道服务端实现的情况下暂时没找到样例,有机会遇见再补上。

    // Init init config client.
    // If confPath is set, it inits file client by default
    // Otherwise we could pass args to init remote client
    // args[0]: driver name, string type
    func Init(args ...interface{}) (err error) {
    	if confPath != "" {
    		DefaultClient, err = NewFile(confPath)
    	} else {
    		var (
    			driver Driver
    		)
    		argsLackErr := errors.New("lack of remote config center args")
    		if len(args) == 0 {
    			panic(argsLackErr.Error())
    		}
    		argsInvalidErr := errors.New("invalid remote config center args")
    		driverName, ok := args[0].(string)
    		if !ok {
    			panic(argsInvalidErr.Error())
    		}
    		driver, err = GetDriver(driverName)
    		if err != nil {
    			return
    		}
    		DefaultClient, err = driver.New()
    	}
    	if err != nil {
    		return
    	}
    	return
    }
    

    体感paladin使用舒适度还是挺不错的、

    断剑重铸之日,骑士归来之时

  • 相关阅读:
    tomcat做成windows服务之后使用JMX监控的问题
    Spring CORS
    人大金仓
    今天遇到奇怪的事:SVN本地代码的标记突然没了,Clean up也报错
    你真得懂Javascript中的==等于运算符吗?
    Codeforces 384E 线段树+dfs序
    一个图形引擎的画面风格是由那些因素(技术)决定的?
    【BZOJ 1146】【CTSC 2008】网络管理network
    ajax核心技术1---XMLHttpRequset对象的使用
    Android中Application类的详解:
  • 原文地址:https://www.cnblogs.com/ailumiyana/p/12122154.html
Copyright © 2011-2022 走看看