前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SCF+腾讯云API+企业微信机器人实现CDB慢查询提醒

SCF+腾讯云API+企业微信机器人实现CDB慢查询提醒

原创
作者头像
树根
修改2019-01-26 13:42:50
4.5K4
修改2019-01-26 13:42:50
举报
文章被收录于专栏:树根的专栏树根的专栏

依赖

背景

CDB数据库在腾讯云控制台可以看到每个数据库示例的操作日志,其中我们可以下载到:

  • 没有使用索引的查询
  • 查询时间超过指定时间的查询

但是,为了找到这些慢查询日志的下载路径,在不介入API调用的情况下,需要在控制台点开每个数据库示例,找到慢查询日志的下载TAB页,进行慢查询日志的下载,在集中对数据库进行索引和查询优化的时候,这种操作是需要一定成本哒。

慢查询日志下载入口
慢查询日志下载入口

机器人方案

为了能够每天早晨或者某个时间段自动汇总所有数据库示例的慢查询日志的下载链接给到开发同学下载统一分析,我们可以利用腾讯云CDB备份相关的接口来完成CDB慢查询日志的获取,汇总所有的查询结果,通过企业微信群机器人WebHook将消息发送到指定群。

方案效果
方案效果

考虑到SCF代码部署的方便性,采用golang来进行功能的开发。

流程

  • 腾讯云API调试工具
  • golang开发IDE/EDITOR

调试获取所有数据库示例接口

在线调试地址:https://console.cloud.tencent.com/api/explorer?Product=cvm&Version=2017-03-12&Action=DescribeInstances

获取示例接口
获取示例接口

点击在线调用选项卡。

返回结果
返回结果

将得到的JSON结果通过JSON转Struct转换成GO语言需要的struct定义(参考下文代码实现)。

调试获取所有某个数据库示例慢查询日志接口

在线调试地址:https://console.cloud.tencent.com/api/explorer?Product=cdb&Version=2017-03-20&Action=DescribeSlowLogs

获取慢查询日志接口
获取慢查询日志接口
返回结果
返回结果

将得到的JSON结果通过JSON转Struct转换成GO语言需要的struct定义(参考下文代码实现)。

测试通过后,将代码集成

  • SCF 支持多线程运行,因此我们可以使用go协程,并行请求CDB示例的慢查询结果,减少SCF运行时间,省钱呐
代码语言:javascript
复制
func main() {
	cloudfunction.Start(MainFunc)
}  
  • main函数有固定写法,需要采用标准姿势,如果不采用标准姿势,会导致SCF执行超时,浪费钱哟

不按照标准姿势的你,可能会遇到SCF在超时结束之后,得到这样的返回值撒

代码语言:txt
复制
{"errorCode":-1,"errorMessage":"Time limit exceeded"}

打包和部署

Golang 环境的云函数,仅支持 zip 包上传,可以选择使用本地上传 zip 包或通过 COS 对象存储引用 zip 包。zip 包内包含的应该是编译后的可执行二进制文件,二进制文件需要在 zip 包根目录,注意打包成zip包的时候不要多了一层文件夹呀。 Golang 编译可以在任意平台上通过制定 OS 及 ARCH 完成跨平台的编译,因此在 Linux,Windows 或 MacOS 下都可以进行编译。 在 Linux 或 MacOS 下通过如下方法完成编译及打包: SCF需要和你的数据库在同一个地域,比如都在广州或者上海GOOS=linux GOARCH=amd64 go build -o main main.go zip main.zip main

在 Windows 下可使用如下命令编译

代码语言:txt
复制
set GOOS=linux
set GOARCH=amd64
go build -o main main.go

然后,就是配置SCF了

SCF 配置,关系到你需要付的成本哟
SCF 配置,关系到你需要付的成本哟
SCF运行的触发条件配置
SCF运行的触发条件配置

程序的简单实现如下:

代码语言:txt
复制
type InstanceResponse struct {
	Response struct {
		TotalCount int `json:"TotalCount"`
		Items      []struct {
			InstanceID    string `json:"InstanceId"`
			ResourceID    string `json:"ResourceId"`
			RegionID      int    `json:"RegionId"`
			RegionName    string `json:"RegionName"`
			QPS           int    `json:"Qps"`
			Region        string `json:"Region"`
			InitFlag      int    `json:"InitFlag"`
			InstanceType  int    `json:"InstanceType"`
			InstanceName  string `json:"InstanceName"`
			Vip           string `json:"Vip"`
			Vport         int    `json:"Vport"`
			WanStatus     int    `json:"WanStatus"`
			WanDomain     string `json:"WanDomain"`
			WanPort       int    `json:"WanPort"`
			Status        int    `json:"Status"`
			CdbError      int    `json:"CdbError"`
			TaskStatus    int    `json:"TaskStatus"`
			EngineVersion string `json:"EngineVersion"`
			CreateTime    string `json:"CreateTime"`
			DeadlineTime  string `json:"DeadlineTime"`
			IsolateTime   string `json:"IsolateTime"`
			DeviceType    string `json:"DeviceType"`
			Memory        int    `json:"Memory"`
			Volume        int    `json:"Volume"`
			CPU           int    `json:"Cpu"`
			AutoRenew     int    `json:"AutoRenew"`
			ZoneID        int    `json:"ZoneId"`
			Zone          string `json:"Zone"`
			ZoneName      string `json:"ZoneName"`
			VpcID         int    `json:"VpcId"`
			SubnetID      int    `json:"SubnetId"`
			UniqVpcID     string `json:"UniqVpcId"`
			UniqSubnetID  string `json:"UniqSubnetId"`
			ProjectID     int    `json:"ProjectId"`
			PayType       int    `json:"PayType"`
			ProtectMode   int    `json:"ProtectMode"`
			DeployMode    int    `json:"DeployMode"`
			SlaveInfo     struct {
				First struct {
					Region string `json:"Region"`
					ZoneID int    `json:"ZoneId"`
					Zone   string `json:"Zone"`
					Vip    string `json:"Vip"`
					Vport  int    `json:"Vport"`
				} `json:"First"`
				Second interface{} `json:"Second"`
			} `json:"SlaveInfo"`
			MasterInfo    interface{}   `json:"MasterInfo"`
			RoInfo        []interface{} `json:"RoInfo"`
			RoGroups      []interface{} `json:"RoGroups"`
			DrInfo        []interface{} `json:"DrInfo"`
			BackupZoneID  int           `json:"BackupZoneId"`
			ExClusterID   string        `json:"ExClusterId"`
			OfflineTime   string        `json:"OfflineTime"`
			HourFeeStatus int           `json:"HourFeeStatus"`
			RoVipInfo     interface{}   `json:"RoVipInfo"`
			PhysicalID    string        `json:"PhysicalId"`
		} `json:"Items"`
		RequestID string `json:"RequestId"`
	} `json:"Response"`
}

type SlowLogResponse struct {
	Response struct {
		TotalCount int `json:"TotalCount"`
		Items      []struct {
			Name        string `json:"Name"`
			IntranetURL string `json:"IntranetUrl"`
			InternetURL string `json:"InternetUrl"`
			Size        int    `json:"Size"`
			Type        string `json:"Type"`
			Date        string `json:"Date"`
		} `json:"Items"`
		RequestID string `json:"RequestId"`
	} `json:"Response"`
}

func main() {
	cloudfunction.Start(MainFunc)
}

func MainFunc() {
	credential := common.NewCredential(
		"",
		"",
	)
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = "cdb.tencentcloudapi.com"
	client, _ := cdb.NewClient(credential, "ap-guangzhou", cpf)

	insRequest := cdb.NewDescribeDBInstancesRequest()
	insParams := `{"Limit":100}`

	err := insRequest.FromJsonString(insParams)
	if err != nil {
		panic(err)
	}

	responseIns, err := client.DescribeDBInstances(insRequest)
	if _, ok := err.(*errors.TencentCloudSDKError); ok {
		fmt.Println(err)
		return
	}
	if err != nil {
		panic(err)
	}

	var response InstanceResponse
	err = json.Unmarshal([]byte(responseIns.ToJsonString()), &response)
	if err != nil {
		fmt.Println(err)
		return
	}

	instanceCount := len(response.Response.Items)

	if instanceCount == 0 {
		fmt.Println("no instance")
		return
	}

	var wg sync.WaitGroup
	wg.Add(instanceCount)

	slowLogChan := make(chan SlowLogResponse, instanceCount)
	for idx := 0; idx < instanceCount; idx++ {
		go SlowLogRequest(response.Response.Items[idx].InstanceID, slowLogChan, &wg)
	}

	wg.Wait()
	close(slowLogChan)

	var msgBuf bytes.Buffer //消息buf
	for res := range slowLogChan {
		if len(res.Response.Items) > 1 && res.Response.Items[1].Size > 0 {
			logName, err := url.QueryUnescape(res.Response.Items[1].Name)
			if err != nil {
				continue
			}

			msgBuf.WriteString(fmt.Sprintf(">Name: %s \\n", logName))
			msgBuf.WriteString(fmt.Sprintf(">Size: %d B\\n", res.Response.Items[1].Size))
			msgBuf.WriteString(fmt.Sprintf("[点击下载](%s) \\n\\n", res.Response.Items[1].InternetURL))
		}
	}

	msgStr := msgBuf.String()
	if msgStr == "<nil>" || msgStr == "" {
		msgStr = "<font color=\"success\">很好~</font>昨天没有产生慢查询日志\\n"
	} else {
		msgStr = "### 昨日CDB慢查询提醒 \\n\\n" + msgStr
	}

	sendMarkdownToUs(msgStr)
	fmt.Println("exit...")
	return
}

func sendMarkdownToUs(msgStr string) {
	param := fmt.Sprintf(`{"msgtype": "markdown",
    "markdown": {
        "content":"%s"
	}}`, msgStr)

	fmt.Println(param)
	resp, err := http.Post("https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的企业微信机器人key",
		"application/json",
		strings.NewReader(param))

	if err != nil {
		fmt.Println(err)
		return
	}

	defer resp.Body.Close()
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("send msg error")
	}

	return
}

func SlowLogRequest(instanceId string, ch chan SlowLogResponse, wg *sync.WaitGroup) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
		wg.Done()
	}()

	credential := common.NewCredential(
		"",
		"",
	)
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = "cdb.tencentcloudapi.com"
	client, _ := cdb.NewClient(credential, "ap-guangzhou", cpf)

	request := cdb.NewDescribeSlowLogsRequest()

	params := fmt.Sprintf(`{"InstanceId":"%s", "Limit":100}`, instanceId)
	err := request.FromJsonString(params)

	if err != nil {
		panic(err)
	}

	response, err := client.DescribeSlowLogs(request)
	if _, ok := err.(*errors.TencentCloudSDKError); ok {
		fmt.Println(err)
		return
	}
	if err != nil {
		panic(err)
	}

	var slowLogResponse SlowLogResponse
	err = json.Unmarshal([]byte(response.ToJsonString()), &slowLogResponse)
	if err != nil {
		fmt.Println(err)
		return
	}

	ch <- slowLogResponse

	return
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 依赖
  • 背景
  • 机器人方案
  • 流程
    • 调试获取所有数据库示例接口
      • 调试获取所有某个数据库示例慢查询日志接口
        • 测试通过后,将代码集成
        • 打包和部署
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档