go yaml解析不定长yaml文件记录

本文介绍了一种自动解析Kubernetes服务及其数据库连接信息的方法,通过分析配置文件,利用Go语言和yaml.v2包实现服务名称与数据库连接地址的提取,适用于多种服务类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在梳理公司的k8s服务和对应数据库连接关系,由于服务太多开发团队也不完全清楚有哪些,于时我决定从服务的配置文件去分析先拉出服务
名称和对应数据库连接地址,同时尝试写成服务的形式监控git上的配置文件路径,若有新的服务自动从中解析出相关数据库连接信息;

服务采用go编写,而本次分享则是服务核心,对不定长yaml文件的解析思路:

环境:
    在config目录下有许多服务配置文件目录,在配置文件的目录下会有yaml配置文件,由于负责发布的人员不同,yaml文件的形式也不一定相同
    比如:有的人喜欢多个配置文件写在一个yaml里的,有的人会写多个yaml;
    同时,由于服务有的不需要连接数据库,所以有的yaml文件没有数据库连接信息;


第三方解析包:
    gopkg.in/yaml.v2
    yaml.v2是目前git上比较火的go语言解析yaml的第三方包,他的解析yaml主要是使用Unmarshal方法,具体后面再讨论


解题思路:
    yaml文件的基本配置有:
    apiVersion
    metadata
    kind
    以上的三个几乎是所有的yaml文件都必带的,spec标签则有的可以代有的不需要,仔细研读服务的配置文件,
    发现配置文件中有数据库连接信息的,都是带有以下形式的标签:
    data:
        application.properties:

    所有的服务name都在metadata下面,所以name比较好取,不好取的是data下边的数据库连接信息,由于公司有.net/java/python
    三种服务在k8s里面跑,连接数据库的方式也不一样,有的会在application标签下有的则不是,但统一都会在data标签下;
    那么解析数据库连接地址时,需要解析data,无法确定data标签的内层结构时,需要做通配符正则匹配;


    如果不用第三方包,直接用正则处理也是可以的,但是无法得到yaml的精准层次结构,所以本次使用第三方包;

go yaml.v2包:

    yaml.v2是一个可以构建或者解析yaml文件的包;Marshal是构建yaml,Unmarshal是解析yaml,构建yaml需要任意类型参数,
    一般都已struct形式构建好有层次结构的参数,而解析需要入参byte类型的yaml数据,同时返回出一个map;

    所以解析yaml的时候,需要给Unmarshal一个map参数让他存储解析返回值;注意,这个map的类型可以是interface类型也可以是
    自定义的struct类型或者常规string类型

源码:
 

package main

import (
	"bytes"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

//声明一个全局存储解析数据库连接配置文件的slice
var configlist []string
//yaml配置文件路径
var path string = "C:\\Users\\lovecat\\Desktop\\configs"
//数据库连接匹配的配置文件路径
var config_path string = "C:\\Users\\lovecat\\Desktop\\configs\\readpath_config.txt"


//声明所需解析yaml的struct,注意namespace和spec其实举例的这个配置yaml配置文件根本没有
//但是并不妨碍我声明一个他的结构,因为有的配置文件有这些,如果我需要namespace,那么我就可以调用,没有namespace的配置文件
//只是返回空值而已,并不会报错
type name struct{
	Name string `yaml:"name"`
	Namespace string `yaml:"namespace"`
}
type yl struct{
	Metadata name `yaml:"metadata"`
    //由于我并不知道data和spec下面的层级结构,但是go-yaml每层返回的都是map,所以直接定义一个
    //map结构的类型来承接,如果定义成map string,那么返回的map中value会全部被转成string类型
    //string类型正则比较好处理,如果想保留原map,只需要定义成map interface{}类型,该map则会
    //保留解析出的原格式,但是实际层级提取不好处理,建议少用interface类型
	Data map[string]string `yaml:"data"`
	Spec map[string]string `yaml:"spec"`
}


func main() {
	var data,new_data []string
	d,_ :=Dir_read(path,data)
	for _,v := range d{
		d,_ = Dir_read(v,d)
	}
	for _,v1 := range d{
		getpath,re := Data_format(v1,"-")
		if re{
			new_data = append(new_data,getpath)
		}
	}

	read_config(config_path)
	//以下循环是遍历所有yaml文件夹下面的yaml,所以省略掉,本次以指定yaml举例
	//for _,v2 := range new_data{
	//	read_yaml(v2)
	//}

	//举例的指定yaml配置文件
	read_yaml("C:\\Users\\lovecat\\Desktop\\configs\\CollectService\\CollectService-D")

}


//入参:地址路径,文件地址分析,不是重点
func Dir_read(indata string,data []string) ([]string,error){
	dataname,err := ioutil.ReadDir(indata)
	if err != nil {
		return data,err
	} else{
		for _,v := range dataname{
			if v.IsDir(){
				new_path := indata+"\\"+v.Name()
				data = append(data,new_path)
			}else{
				_,bo := Data_format(v.Name(),".")
				if bo{
					data = append(data,indata)
				}
			}
		}
		return data,err
	}
}

//入参:文件名,切分字符 出参:文件指针,是否为指定文件
//格式化配置文件夹路径下的文件,由于配置文件夹下有多种文件,所以我这里要解析一次,不是yaml解析的重点
func Data_format(indata,sptype string) (string,bool){

	datalist := strings.Split(indata,sptype)
	fi := datalist[len(datalist)-1]
	switch sptype {
	case  "-":
		if  strings.ToUpper(fi) == "D"{
			return indata,true
		}else {
			return fi, false
		}
	case  ".":
		if  strings.ToUpper(fi) == "YAML"{
			return indata,true
		}else {
			return indata, false
		}
	case "\\":
		return fi,true
	default:
		return fi,false
	}
}


//读取配置文件,读取的是数据库匹配规则配置文件
func read_config(inpath string) ([]string,error){
	file,err := os.Open(inpath)
	if err != nil{
		return configlist,err
	}
	defer file.Close()
	con,err := ioutil.ReadAll(file)
	b := strings.Split(string(con),",\r\n")
	for _,v := range b{
		configlist = append(configlist,v)
	}
	return configlist,err
}


//读取yaml文件,解析yaml文件
func read_yaml(inpath string) {

	//yaml文件换成列表声明
	var filelist []string

	//获取入参路径下的文件并判断是yaml文件则该文件路径添加到yaml列表中
	dataname,err := ioutil.ReadDir(inpath)
	if err != nil {
	}
	for _,v:= range dataname{
		_,b :=Data_format(v.Name(),".")
		if b {
			file := inpath+"\\"+v.Name()
			filelist = append(filelist,file)
		}
	}

	//遍历yaml列表中yaml文件并且获取指定数据匹配
	for _,f := range filelist{
		file,err := os.Open(f)
		if err != nil{
		}
		defer file.Close()
		//读取yaml文件数据
		yaml_con,err := ioutil.ReadAll(file)

		//切分yaml文件为独立文件
		yaml_split := []byte("---")
		con1 := bytes.Split(yaml_con,yaml_split)

		//遍历独立apiversion
		for _,yl_split := range con1{
			//新建换成yaml数据的map,te可以构建成make(map[interface]interface{})或者make(map[string]interface{}) 或者struct
			//建议解析yaml统一构建成struct,如果不定的标签,再struct中把他定义成interface{}的map或者string类型的map即可,若配置文件没有该
			//标签,则该标签返回为空指针,并不会报错,简单的来说多定义struct参数,多的包含少的,即使yaml配置文件没有该参数也不会报错,如果定义少了
			//没有数据存储,解析出来的文件反而使用更加不便
			//例:te := make(map[string]interface{})/te := make(map[interface{}]interface{})/建议少使用interface{}类型
			var te yl
			err = yaml.Unmarshal(yl_split,&te)

			//遍历匹配的配置文件
			for _,v :=range configlist{
				ok,_ := regexp.Match(v,yl_split)
				if ok{
					//解析yaml
					//err = yaml.Unmarshal(yl_split,&te)

					for _,v1 := range te.Data{
						//构建正则规则,匹配mysql和对应具体的数据库
						var  ex_db_url string = v+"/.*\\?"
						db_url := regexp.MustCompile(ex_db_url).FindString(v1)
						//如果为空字符则连接的是oracle,没有具体的数据库指向
						if db_url == "" {
							ex_db_url = v
							db_url = regexp.MustCompile(ex_db_url).FindString(v1)
						}
						//判断连接归属
						var db_type string
						//以下已屏蔽链接地址
						switch v {
						case "A":
							db_type = "阿里云RDS"
						case "B":
							db_type = "阿里云RDS"
						case "C":
							db_type = "阿里云RM"
						case "D":
							db_type = "阿里云RM"
						case "E":
							db_type = "北艾NDB"
						case "F":
							db_type = "北艾8.0"
						case "G":
							db_type = "北艾5.7"
						case "H":
							db_type = "Oracle Master"
						case "I":
							db_type = "Oracle Slave"
						default:
							db_type = "未配置"
						}

						fmt.Println("服务名称:"+te.Metadata.Name+",","数据库连接地址:"+db_url+",","数据库实例所属:"+db_type+",","配置文件路径:"+inpath)
					}

				}
			}
		}
	}

}

 

 

数据库配置文件,信息已处理过,只代表格式:

 

rm-kgkytukll.mysql.rds.aliyuncs.com:3306,
rm-dhdhfjghk.mysql.rds.aliyuncs.com:3306,
adagfhfhjg.mysql.rds.aliyuncs.com:3306,
sddhthjjgsg.mysql.rds.aliyuncs.com:3306,
172.40.51.22:3306,
172.40.51.31:3310,
172.40.51.31:3311,
172.40.51.50:1521,
172.40.51.51:1521

yaml配置文件,数据已修改,只代表格式:

apiVersion: v1
kind: ConfigMap
metadata:
  name: collectservice
data:
  application.properties: |
    spring.application.name=CollectService
    server.port=80
    spring.datasource.mysql.url=jdbc:mysql://172.40.51.31:3310/collectdb?useUnicode=&=UTF-8&=&=
    spring.datasource.mysql.cryptid=collectservice



---
apiVersion: v1
kind: Service
metadata:
  name: collectservice
spec:
  selector:
    app: collectservice
  ports:
    - name: http
      port: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: collectservice
  labels:
    app: collectservice
spec:
  selector:
    matchLabels:
      app: collectservice
  replicas: 1
  template:
    metadata:
      labels:
        app: collectservice
    spec:
      containers:
      - name: collectservice
        image: 172.98.35.26:5000/collectservice:54657696
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
        livenessProbe:
          httpGet:
            path: /keeplive
            port: 80
          failureThreshold: 3
          initialDelaySeconds: 30
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 3
        readinessProbe:
          httpGet:
            path: /keeplive
            port: 80
          initialDelaySeconds: 20
          periodSeconds: 10
          timeoutSeconds: 5
        resources:
          requests:
            memory: 1Gi
          limits:
            memory: 4Gi
        volumeMounts:
          - name: config
            mountPath: /application.properties
            subPath: application.properties
      volumes:
        - name: config
          configMap:
            name: collectservice

输出结果:

GOROOT=C:\Go #gosetup
GOPATH=C:\Users\lovecat\go;C:\Users\lovecat\Desktop\go\golang #gosetup
C:\Go\bin\go.exe build -o C:\Users\lovecat\AppData\Local\Temp\___go_build_main_go.exe C:/Users/lovecat/OneDrive/MyProgram/ServerProgram/GoProgram/Hires.com/main.go #gosetup
C:\Users\lovecat\AppData\Local\Temp\___go_build_main_go.exe #gosetup
服务名称:collectservice, 数据库连接地址:172.40.51.31:3310/collectdb?, 数据库实例所属:未配置, 配置文件路径:C:\Users\lovecat\Desktop\configs\CollectService\CollectService-D

Process finished with exit code 0

总结:

很明显,这个yaml写了三个pod的配置文件,所以规则中我是写了切分之后再循环解析的,对于yaml的不定长解析方式,

建议构建struct并尽量多构建几个已知的结构,对于不确定的结构,构建成map[string]interface{}或者map[interface{}]interface{}来存储即可,interface{}的断言不是很好处理,建议能不用就不要用.

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值