背景

由于后端微服务架构,于是各种业务被拆分为多个服务,服务之间的调用采用RPC接口,而Nacos作为注册中心,可以监听多个服务的状态,比如某个服务是否down掉了、某个服务的访问地址是否改变、以及流量分配降级等等。(nacos注册中心的由来及原理

运维架构用的是K8S,由于之前没有做服务高可用的特性,K8S上服务挂掉后不会自动重启。

所以测试过程中,某个接口有问题,可能最先需要排查该服务或依赖服务是否正常可用。

问题

上面说到,测试过程中还需要查看服务是否正常,这是很浪费时间的,特别是环境不稳定的情况下,也许今天下班前测得好好的,明天或者后天就会发现部分服务就挂掉了(毕竟六七十个服务人工筛查太麻烦了)。能否把这个问题前置解决?

解决方案

我的解决方法就是,利用python爬虫思路,每天获取当前nacos注册的服务与nacos配置的服务列表对比,如果注册的服务小于Nacos的服务,就说明部分服务不可用,这时就可以先解决后再介入测试,再搭配Jenkins,可以早晚巡查服务是否正常。(当然我们后面K8S已经实现了服务的自动重启,所以该脚本也算服务检查的补充方案了)

设计如下

首先定义一个测试类checkJob。

第一步,登录nacos

1     # 登录nocas
2 def con_nacos(self):
3 data = {"username":"nacos","password":"Billion123!@#"}
4 res = requests.post(url = self.urlLogin, headers = self.headers, data=data).json()['accessToken']
5 return res

第二步,获取当前namespace服务配置文件

1     # 获取目前环境服务配置列表
2 def get_job_config(self):
3 self.joblist = []
4 params = self.config_params
5 res = requests.get(url= self.urlGetConfig, headers = self.headers, params = params).json()["pageItems"]
6 for i in res:
7 self.joblist.append(i.get("dataId"))
8 print("-------目前的服务配置有:", len(self.joblist))
9 return self.joblist

第三步,获取当前正常注册的服务

1     # 获取目前所有服务列表
2 def get_job(self):
3 self.getjoblist = []
4 res = requests.get(url = self.urlGetJob,headers = self.headers, params = self.params).json()['serviceList']
5 for i in res:
6 self.getjoblist.append(i.get("name"))
7 return self.getjoblist

第四步,比对实际注册的服务与配置的服务并打印输出

 1     #迭代比对当前服务列表
2 def check_job(self):
3 tmp = set(self.joblist).difference(self.getjoblist)
4 if tmp:
5 return tmp
6 else:
7 return ""
8
9 #对未存在的服务通知告警
10 def msg(self,tmp):
11 for i in self.ignore:
12 if i in tmp:
13 tmp.remove(i)
14 # print(i)
15 if tmp:
16 print(f"缺少{len(tmp)}个服务,如下:", tmp)
17 else:
18 print(f"{len(self.joblist)}个服务正常:", self.joblist)

当然可能有多个环境,优化一下代码,全部代码如下:

  1 import requests
2
3 class CheckJob():
4 #定义所有服务列表
5 joblist = []
6 urlLogin = "http://xxx.xxx.xxx.xxx:10086/nacos/v1/auth/users/login"
7 urlGetConfig = "http://xxx.xxx.xxx.xxx:10086/nacos/v1/cs/configs"
8 urlGetJob = "http://xxx.xxx.xxx.xxx:10086/nacos/v1/ns/catalog/services"
9 headers = {
10 'Accept': 'application/json, text/plain, */*',
11 'Accept-Encoding': 'gzip, deflate',
12 'Accept-Language': 'zh-CN,zh;q=0.9',
13 'Connection': 'keep-alive',
14 "Host": "nacos.bb.local",
15 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
16 }
17 # 忽略部分服务列表
18 ignore = ["member", "zuul", "order", "b-shop"]
19 def __init__(self):
20 self.accessToken = self.con_nacos()
21 for str in ["d","t","p","pro"]:
22 if str.strip() == "p":
23 # 预发环境
24 self.params = "hasIpCount=true&withInstances=false&pageNo=1&pageSize=100&serviceNameParam=&groupNameParam=&namespaceId=4dcd1ab3-9c07-4f2b-9ed9-9c1f67f4de9e&accessToken=$accessToken"
25 self.config_params = "dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=100&tenant=4dcd1ab3-9c07-4f2b-9ed9-9c1f67f4de9e&search=accurate&accessToken=$accessToken"
26 print("预发环境",end="")
27 elif str.strip() == "t":
28 # 测试环境
29 self.params = "hasIpCount=true&withInstances=false&pageNo=1&pageSize=100&serviceNameParam=&groupNameParam=&namespaceId=0f4106a4-acad-4327-a1f5-54ee78b342ea&accessToken=$accessToken"
30 self.config_params = "dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=100&tenant=0f4106a4-acad-4327-a1f5-54ee78b342ea&search=accurate&accessToken=&accessToken"
31 print("测试环境", end="")
32 elif str.strip() == "d":
33 # 开发环境
34 self.params = "hasIpCount=true&withInstances=false&pageNo=1&pageSize=100&serviceNameParam=&groupNameParam=&accessToken=$accessToken&namespaceId=ddd4b051-5a76-417d-bef8-09f7ff941bbd"
35 self.config_params = "dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=100&tenant=ddd4b051-5a76-417d-bef8-09f7ff941bbd&search=accurate&accessToken=&accessToken"
36 print("开发环境",end="")
37 elif str.strip() == "pro":
38 # 线上环境
39 self.params = "hasIpCount=true&withInstances=false&pageNo=1&pageSize=100&serviceNameParam=&groupNameParam=&accessToken=&accessToken&namespaceId=1b1a6ba8-2d21-4cd6-b7b2-ecd3b838c387"
40 self.config_params = "dataId=&group=&appName=&config_tags=&pageNo=1&pageSize=100&tenant=1b1a6ba8-2d21-4cd6-b7b2-ecd3b838c387&search=accurate&accessToken=&accessToken"
41 print("线上",end="")
42 else:
43 print("输入不合法")
44 if self.params:
45 self.params = self.params.replace("$accessToken", self.accessToken)
46 if self.get_job_config():
47 if self.get_job():
48 self.msg(self.check_job())
49 else:
50 print("获取nacos服务失败")
51 else:
52 print("获取nacos服务配置失败")
53 else:
54 print("nacos登录失败")
55
56
57 # 登录nocas
58 def con_nacos(self):
59 data = {"username":"naco登录名","password":"nacos登录密码"}
60 res = requests.post(url = self.urlLogin, headers = self.headers, data=data).json()['accessToken']
61 return res
62
63 # 获取目前环境服务配置列表
64 def get_job_config(self):
65 self.joblist = []
66 params = self.config_params
67 res = requests.get(url= self.urlGetConfig, headers = self.headers, params = params).json()["pageItems"]
68 for i in res:
69 self.joblist.append(i.get("dataId"))
70 print("-------目前的服务配置有:", len(self.joblist))
71 return self.joblist
72
73 # 获取目前所有服务列表
74 def get_job(self):
75 self.getjoblist = []
76 res = requests.get(url = self.urlGetJob,headers = self.headers, params = self.params).json()['serviceList']
77 for i in res:
78 self.getjoblist.append(i.get("name"))
79 return self.getjoblist
80
81 #迭代比对当前服务列表
82 def check_job(self):
83 tmp = set(self.joblist).difference(self.getjoblist)
84 if tmp:
85 return tmp
86 else:
87 return ""
88
89 #对未存在的服务通知告警
90 def msg(self,tmp):
91 for i in self.ignore:
92 if i in tmp:
93 tmp.remove(i)
94 # print(i)
95 if tmp:
96 print(f"缺少{len(tmp)}个服务,如下:", tmp)
97 else:
98 print(f"{len(self.joblist)}个服务正常:", self.joblist)
99
100 if __name__ == '__main__':
101 cj= CheckJob()

Jenkins持续集成

第一步,上传到gitlab

这一步就不说了,自行上传即可(记得拷贝git链接),没用gitlab的可以直接放到Jenkins的服务器上。

第二步,配置jenkins

新建一个任务,选择github项目,输入项目地址

在源码管理这里配置的git登录用户证书信息,分支直接选master即可

构建触发器这里填入cron定时任务,这里代表周一到周六每天早上8点和下午15点各执行一次。

构建命令这里输入python命令即可(注意Jenkins的服务器上得有python的运行环境)

最后,构建后自动发送邮件(需要安装Editable Email Notification插件)

以下收件人和回复人得邮件地址采用了变量方式,也可直接输入邮箱地址

Content Type选择HTML(text/html)作为邮箱格式,Default Subject输入模板标题,Default Content作为邮件内容模板,参考如下

 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="UTF-8">
5 <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次构建日志</title>
6 </head>
7
8 <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4"
9 offset="0">
10 <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
11 <tr>
12 本邮件由系统自动发出,无需回复!<br/>
13 各位同事,大家好,以下为${PROJECT_NAME }项目构建信息</br>
14 <td><font color="#2D5900">构建结果 - ${BUILD_STATUS}</font></td>
15 </tr>
16 <tr>
17 <td><br />
18 <b><font color="#0B610B">构建信息</font></b>
19 <hr size="2" width="100%" align="center" /></td>
20 </tr>
21 <tr>
22 <td>
23 <ul>
24
25 <li>账 号 : admin</li>
26 <li>密 码 : admin123</li>
27 <li>项目名称 : ${PROJECT_NAME}</li>
28 <li>构建编号 : 第${BUILD_NUMBER}次构建</li>
29 <li>触发原因: ${CAUSE}</li>
30 <li>构建状态: ${BUILD_STATUS}</li>
31 <li>项目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li>
32 <li>构建信息: ${BUILD_LOG, maxLines = 10, escapeHtml = false}</li>
33 </ul>
34
35 <h4><font color="#0B610B">失败用例</font></h4>
36 <hr size="2" width="100%" />
37 $FAILED_TESTS<br/>
38
39 <h4><font color="#0B610B">最近提交(#$SVN_REVISION)</font></h4>
40 <hr size="2" width="100%" />
41 <ul>
42 ${CHANGES_SINCE_LAST_SUCCESS, reverse=true, format="%c", changesFormat="<li>%d [%a] %m</li>"}
43 </ul>
44 </td>
45 </tr>
46 </table>
47 </body>
48 </html>

Attachments作为邮件附件,直接输入report/*.html,Attach Build Log这栏选择Attach Build Log选项

保存构建项目,查看结果如下,详细配置邮件可参考这个文章:https://zhuanlan.zhihu.com/p/161627231

微服务状态之python巡查脚本开发的更多相关文章

  1. 通过Dapr实现一个简单的基于.net的微服务电商系统(十四)——开发环境容器调试小技巧

    之前有很多同学提到如何做容器调试,特别是k8s环境下的容器调试,今天就讲讲我是如何调试的.大家都知道在vs自带的创建项目模板里勾选docker即可通过F5启动docker容器调试.但是对于启动在k8s ...

  2. zipkin微服务调用链分析(python)

    一,概述 zipkin的作用 在微服务架构下,一个http请求从发出到响应,中间可能经过了N多服务的调用,或者N多逻辑操作,如何监控某个服务,或者某个逻辑操作的执行情况,对分析耗时操作,性能瓶颈具有很 ...

  3. Spring cloud微服务安全实战-4-11Zuul网关安全开发(四)

    限流,有个现成的开源项目可以帮助我们来做网关上的限流 用最新的这个版本 在pom.xml加入引用. 在限流的过程中需要存一些信息,可以存在数据库里 也可以存在redis里.这里我们演示存到数据库里 比 ...

  4. Spring cloud微服务安全实战-4-9Zuul网关安全开发(二)

    把在微服务里面写的安全的相关逻辑挪到网关里面来.这样把安全逻辑和业务逻辑解耦开.那么这些问题就都解决了. 先来看下之前的安全的代码,首先在之类做了认证,认证服务器去认证,拿这个token去换用户信息. ...

  5. Spring cloud微服务安全实战-4-8Zuul网关安全开发(一)

    安全相关的代码和业务逻辑相关的代码实际上是在一个应用里面的,在这个应用里面,我们需要去,这个应用本身的处理逻辑里面需要去处理令牌和用户信息之间的转换. 然后我们需要去知道认证服务器的地址,这些都是耦合 ...

  6. Spring cloud微服务安全实战-4-10Zuul网关安全开发(三)

    首先把地址给它 发送post请求,请求的数据就是这个entity对象. 最后返回的值要封装到TokenInfo里面 如果一切正常的话就会拿到一个响应的实体,实体里面就包含了TokenInfo 打印实体 ...

  7. 基于DDD的微服务设计和开发实战

    你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴 ...

  8. 驱动领域DDD的微服务设计和开发实战

    你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务设计和开发实战篇,通过借鉴 ...

  9. 关于Python构建微服务的思考(一)

    一:什么是微服务? 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成. 系统中的各个微服务可被独立部署,各个微服务之间是松耦合的. 每个微服务仅关注于完成一件任务并很好地完成该任务. ...

随机推荐

  1. ModSecurity的规则

    一.ModSecurity的规则 基本格式 SecRule VARIABLES OPERATOR ACTIONS SecRule:ModSecurity主要的指令,用于创建安全规则. VARIABLE ...

  2. Java Study day3

    Day 2 遗留问题: cmd javac编译和 java执行 程序输出Hello World 遗留问题解答: 首先使用Txt记事本写一个后缀为.java的HelloWorld.java程序,直接复制 ...

  3. mmap代替通用IO读取文件数据(curious)

    提供一份测试demo: #include <stdio.h> #include <string.h> #include <stdlib.h> #include &l ...

  4. var、let和const区别

    var.let和const区别 变量提升问题 var声明的变量存在变量提升,而let与const声明的变量不存在变量提升,但存在暂时性死区 即在预编译阶段,js引擎扫描代码时,遇到变量声明,会把var ...

  5. Java 中用到的线程调度算法是什么?

    计算机通常只有一个 CPU,在任意时刻只能执行一条机器指令,每个线程只有获得 CPU 的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线 程轮流获得 CPU 的使用权,分别执行各自 ...

  6. mysql问题排查与性能优化

     MySQL 问题排查都有哪些手段? 使用 show processlist 命令查看当前所有连接信息. 使用 explain 命令查询 SQL 语句执行计划. 开启慢查询日志,查看慢查询的 SQL. ...

  7. 激光雷达 LOAM 论文 解析

    转自:https://blog.csdn.net/hltt3838/article/details/109261334 固态激光雷达的一段视频:https://v.qq.com/x/page/a078 ...

  8. IdentityServer4系列 | 支持数据持久化

    一.前言 在前面的篇章介绍中,一些基础配置如API资源.客户端资源等数据以及使用过程中发放的令牌等操作数据,我们都是通过将操作数据和配置数据存储在内存中进行实现的,而在实际开发生产中,我们需要考虑如何 ...

  9. 如何制作icon-font小图标

    1.首先可以去iconfont.cn阿里巴巴矢量字体库中下载你想要的图标(选择格式为SNG格式). 2.打开iconmoon这个网站(这个样子的),然后点击右上角那个Iconfont App如下图: ...

  10. 基于react的audio组件

    样式请自己定义哦~需要其他功能请自行添加! // 组件调用 <Audio src={src地址} id={srcID}/> audio属性 src 歌曲的路径 preload 是否在页面加 ...