【Golang】基于录制,自动生成go test接口自动化用例
目录
背景
框架
ginkgo初始化
抓包&运行脚本
目录说明
∮./business
∮./conf
∮./utils
∮./testcase
testcase 用例目录结构规则
¶示例
实现思路
解析Har数据
定义结构体
解析到json
转换请求数据
转换请求
转换请求参数
写业务请求数据
写gotest测试用例数据
初始化写入suit文件
格式化测试文件
install生成的业务请求目录
格式化响应断言
可能遇到的问题
完整代码
详细代码如下,注释已经给得比较清晰:
资料获取方法
背景
之前写过一篇博客,介绍怎么用Python通过解析抓包数据,完成自动化用例的编写。最近这段时间在使用go test,所以就在想能不能也使用代码来生成自动化用例,快速提升测试用例覆盖率。说干就干。
框架
首先介绍一下我们使用的测框架:
项 | 信息 | 安装 | 备注 |
---|---|---|---|
GO版本 | go1.12.9 darwin/amd64 | 略 | |
测试框架 | ginkgo | go get -u github.com/onsi/ginkgo/ginkgo | |
断言库 | testify/assert | go get github.com/stretchr/testify | 官方配套的断言库是gomega |
ginkgo初始化
- 初始化:
cd path/to/package/you/want/to/test && ginkgo bootstrap
- 创建示例用例:
ginkgo generate
(需要手动添加测试用例) - 运行测试:
go test
orginkgo
注:-v
加上参数可打印运行信息
抓包&运行脚本
- 使用抓包工具(如Charles)抓包,把数据包导出为har格式,保存在当前目录下
- 如何安装抓包工具在本文就不赘述了,抓包,过滤出想要的数据,导出,保存的格式注意选择为
har
:
- 如何安装抓包工具在本文就不赘述了,抓包,过滤出想要的数据,导出,保存的格式注意选择为
- 根据实际情况修改全局变量信息,如bizBaseFolder、serverName、userFile等
- 使用
go run gentest.go
运行脚本即可
目录说明
然后我们一起来了解一下我们的目录结构定义。
∮./business
业务封装,封装具体的请求及测试数据
∮./conf
配置信息及接口请求参数初始化封装
∮./utils
公共函数封装
∮./testcase
接口测试用例目录
testcase 用例目录结构规则
基本原则: 根据项目、模块、接口功能逐级区分,建议最多3层目录层级
¶示例
- 软件测试论坛项目组/论坛项目/帖子模块/创建帖子接口:
- CN_TestBBS/bbs/post/post_test.go
- 基础账号项目/首页项目/白名单接口:
- CN_account/homepage/whitelist_test.go
实现思路
按照har文件的JSON结构定义对应的结构体,然后解析数据,生成请求数据,生成断言数据,初始化测试套suite,格式化代码,初始化包引用信息。
解析Har数据
定义结构体
Log struct {version stringcreator stringEntries []struct {startedDateTime stringtime stringRequest struct {...
解析到json
func UnpackHar(har []byte) (logs *Har) {err := json.Unmarshal(har, &logs)if err != nil {fmt.Println(err)}return
}
转换请求数据
转换请求
转换请求参数
GET
// 格式化请求参数为标准请求string
getReqParam := make(map[string]interface{}, 1)
if len(v.Request.QueryString) > 0 {for _, query := range v.Request.QueryString {getReqParam[query.Name] = query.Value}
}
// 获取postReq数据
postReqParamStr := v.Request.PostData.Textif v.Request.Method == "GET" {paramstr = genGetParam(InterfaceName, getReqParam)
}
func genGetParam(interfaceName string, param map[string]interface{}) (formatParam string) {// 对于请求参数的value值为 数组if len(param) > 0 {for k, v := range param {switch vv := v.(type) {case []interface{}:fmt.Sprintf(k, "is an array:", vv)temp, _ := json.Marshal(param)formatParam = fmt.Sprintf("%sParam = `%s`", interfaceName, fmt.Sprintf("%v", string(temp)))returndefault:// fmt.Println(k, "is of a type didn't handle")}}}temp, _ := json.Marshal(param)formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, fmt.Sprintf("%v", string(temp)))return
}
POST
postReqParamStr := v.Request.PostData.Textif v.Request.Method == "POST" {paramstr = genPostParam(InterfaceName, postReqParamStr)
}
func genPostParam(interfaceName string, postReqParamStr string) (formatParam string) {// formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, param)// fmt.Sprintf("%v", string(temp))postReqParam := make(map[string]interface{}, 1)if len(postReqParamStr) > 0 {// 判断第一个字符是否为{}, 做传递数据为数组[]的兼容if []rune(postReqParamStr)[0] == '{' {var x interface{}err := json.Unmarshal([]byte(postReqParamStr), &x)if err != nil {fmt.Println("err", err)}postReqParam = x.(map[string]interface{})// fmt.Println(postReqParam)// 判断value中是否存在数组for k, v := range postReqParam {switch vv := v.(type) {// switch vv := v.(type) {case []interface{}:fmt.Sprintf(k, "is an array:", vv)// param[k] = fmt.Sprintf("`%s`", vv)temp, _ := json.Marshal(postReqParam)formatParam = fmt.Sprintf("%sParam = `%s`", interfaceName, fmt.Sprintf("%v", string(temp)))paramType = "string"returndefault:formatParam = genGetParam(interfaceName, postReqParam)// fmt.Println(k, "is of a type didn't handle")}}// 如果为数组,做如下处理} else {var y []interface{}err := json.Unmarshal([]byte(postReqParamStr), &y)if err != nil {fmt.Println("err", err)}postReqParam = y[0].(map[string]interface{})temp, _ := json.Marshal(postReqParam)// 声明请求类型paramType = "[]map[string]interface{}"formatParam = fmt.Sprintf(`%sParam =[]map[string]interface{}{%s}`, interfaceName, string(temp))// 无法使用 判断类型 Param := utils.MapDeepCopy(Hebinz123.XlppcPlaylistApiV1RemarkDelParam)}}// temp, _ := json.Marshal(param)// formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, fmt.Sprintf("%v", string(temp)))return
}
写业务请求数据
写gotest测试用例数据
格式化请求参数为标准请求string。
初始化写入suit文件
这里有一个注意点,Test
后紧接的数据必须是大写。
格式化测试文件
使用goimports
库初始化导入数据包。
install生成的业务请求目录
使用go install
目录生成导入业务请求目录
格式化响应断言
使用类型判断格式化接口返回数据为标准断言string。
可能遇到的问题
- 初始化读取文件的存储buf的size和其实际大小不一致时,json 解析出错“invalid character '\x00' after top-level value”
- go install 执行失败,导致测试用例无法找到其依赖包
- get请求,post请求参数在har文件中的存储方式不一致,获取数据的方式差别很大
- 域名及接口命名规则不一致,
-
、.
等等风格不一致 - 测试suite 紧接Test后方的字符需为大写的字母,否则服务无法被发现,所以需要做大小写转换
完整代码
详细代码如下,注释已经给得比较清晰:
package mainimport ("encoding/base64""encoding/json""fmt""os""os/exec""path/filepath""strings"
)var (baseDomain = "test.bbs.com" // 测试域名,用于切割出请求路径bizBaseFolder = "business/CN_bbs" //业务请求目录testCaseBaseFolder = "testcase/CN_bbs" // 测试用例目录serverName = "cinecismGo" // 服务名paramType = ""
)func main() {userFile := "20190917-cinecismgo.har" // 抓包文件地址fl, err := os.Open(userFile)if err != nil {fmt.Println(userFile, err)return}defer fl.Close()// 读取har数据fileInfo, err := fl.Stat()buf := make([]byte, fileInfo.Size()) // “invalid character '\x00' after top-level value”fl.Read(buf)data := UnpackHar(buf)for _, v := range data.Log.Entries {// 每一个循环初始化请求参数类型paramType = "map[string]interface{}"paramstr := ""// 初始化 请求path,生成标准请求接口名称pathStr, path := initPath(v.Request.URL)InterfaceName := formatInterfaceName(pathStr)// 格式化请求参数为标准请求stringgetReqParam := make(map[string]interface{}, 1)if len(v.Request.QueryString) > 0 {for _, query := range v.Request.QueryString {getReqParam[query.Name] = query.Value}}// 获取postReq数据postReqParamStr := v.Request.PostData.Textif v.Request.Method == "GET" {paramstr = genGetParam(InterfaceName, getReqParam)}if v.Request.Method == "POST" {paramstr = genPostParam(InterfaceName, postReqParamStr)}// 格式化接口返回数据为标准断言stringtext, _ := base64.StdEncoding.DecodeString(v.Response.Content.Text)responseAssertStr := initAssert(text)// 创建业务请求文件、测试用例文件run(serverName, path, InterfaceName, v.Request.Method, responseAssertStr, paramstr)// 【待补充】handle Headers数据// fmt.Println(initHeaders(data))}
}func initAssert(text []byte) (responseAssertStr string) {if len(text) > 0 {var Response interface{}err := json.Unmarshal(text, &Response)if err != nil {fmt.Println("err", err)}responseMap := Response.(map[string]interface{})res := []string{}for k, v := range responseMap {switch vv := v.(type) {case string:// fmt.Println(k, "is string", vv)res = append(res, fmt.Sprintf("%s, _ := js.Get(\"%s\").String() \n assert.Equal(%s, `%v`)", k, k, k, string(vv)))case int64:// fmt.Println(k, "is int", vv)res = append(res, fmt.Sprintf("%s, _ := js.Get(\"%s\").Int() \n assert.Equal(%s, %v)", k, k, k, string(vv)))case float64:// fmt.Println(k, "is float64", vv)res = append(res, fmt.Sprintf("%s, _ := js.Get(\"%s\").Int() \n assert.Equal(%s, %v)", k, k, k, vv))case bool:// fmt.Println(k, "is bool", vv)res = append(res, fmt.Sprintf("%s, _ := js.Get(\"%s\").Bool() \n assert.Equal(%s, %v)", k, k, k, vv))case []interface{}:// fmt.Println(k, "is an array:", vv)res = append(res, fmt.Sprintf("// Key【%s】的子层级的value值未生成断言,系多层级数组数据,具体值如下:", k))res = append(res, fmt.Sprintf("// %v ", vv))case map[string]interface{}:// fmt.Println(k, "is an map:", vv)temp, _ := json.Marshal(vv)res = append(res, fmt.Sprintf("// Key【%s】的子层级value值未生成断言,系多层级Map数据,具体值如下:", k))res = append(res, fmt.Sprintf("// %v ", string(temp)))default:// fmt.Println(k, "is of a type didn't handle", vv)}responseAssertStr = strings.Join(res, "\n")}}return
}func initPath(URL string) (pathStr, path string) {pathStr = strings.Split(URL, baseDomain)[1]if strings.Contains(pathStr, "?") {pathStr = strings.Split(pathStr, "?")[0]path = strings.Split(pathStr, "?")[0]} else {path = pathStr}if strings.Contains(pathStr, ".") {pathStr = strings.Replace(pathStr, ".", "/", 10)pathStr = strings.Replace(pathStr, "-", "/", 10)}// fmt.Println(path)// fmt.Println("pathStr", pathStr)return
}func run(serverName, path, InterfaceName, method, responseAssertStr string, Param string) {// 初始化测试文件InterfaceFilepath := filepath.Join(bizBaseFolder, serverName)Testcasefilepath := filepath.Join(testCaseBaseFolder, serverName)InterfaceFileame := InterfaceName + ".go"Testcasefilename := InterfaceName + "_test.go"// 创建并写入标准请求信息file, err := createFile(InterfaceFilepath, InterfaceFileame)if err != nil {fmt.Println("createInterfaceFile", err)}writeParam(file, serverName, []string{Param})writeReq(file, InterfaceName, path, method)defer file.Close()// 创建并写入测试用例信息file1, err := createFile(Testcasefilepath, Testcasefilename)if err != nil {fmt.Println("createTestcasefile", err)}// 写入suit文件initTestsuit(serverName)// 写入测试用例writeTestcase(file1, serverName, InterfaceName, responseAssertStr)defer file1.Close()// 格式化测试文件exec.Command("goimports", "-w", InterfaceFilepath).Run()exec.Command("goimports", "-w", Testcasefilepath).Run()// 导入InterfaceFilepathexec.Command("go", "install", InterfaceFilepath).Run()
}func initHeaders(har *Har) map[string]string {var headers = make(map[string]string)// fmt.Println(len(har.Log.Entries[0].Request.Headers))for _, v := range har.Log.Entries[0].Request.Headers {headers[v.Name] = v.Value}return headers
}func createFile(filepaths, filename string) (file *os.File, err error) {os.MkdirAll(filepaths, 0777)file, err = os.Create(filepath.Join(filepaths, filename))return
}func createInterfaceFile(path, filename string) (file *os.File, err error) {filename = filename + ".go"filepath := bizBaseFolder + "/" + path + "/"os.MkdirAll(filepath, 0777)file, err = os.Create(filepath + filename)return
}func createTestcasefile(path, filename string) (file *os.File, err error) {filename = filename + "_test.go"filepath := testCaseBaseFolder + "/" + path + "/"os.MkdirAll(filepath, 0777)file, err = os.Create(filepath + filename)return
}func initTestsuit(serverName string) {filename := serverName + "_suite_test.go"filepath := testCaseBaseFolder + "/" + serverName + "/"os.MkdirAll(filepath, 0777)file, err := os.Create(filepath + filename)if err != nil {fmt.Println("initTestsuit Error", err)}// Testsuite后的 首字母需大写,否则suite无法正常检索到testcasefile.WriteString(fmt.Sprintf(`package %s_testimport ("testing". "github.com/onsi/ginkgo". "github.com/onsi/gomega")func Test%s(t *testing.T) {RegisterFailHandler(Fail)RunSpecs(t, "%s Suite")}`, serverName, Capitalize(serverName), serverName))
}func writeTestcase(file *os.File, serverName, InterfaceName, responseAssertStr string) {// 接口引入路径 【服务名称.接口名称】interfaceImportPath := serverName + "." + InterfaceName// 接口标准请求参数 【接口名称Param】paramImportPath := interfaceImportPath + "Param"// 接口标准请求参数拷贝,请求参数为非标准【map[string]interface{}】类型时,该参数为空tempParamStr := ""// 是否使用mapDeepCopy,请求参数为非标准【map[string]interface{}】类型时 使用mapDeepCopy := ""if paramType != "map[string]interface{}" {tempParamStr = paramImportPath}if paramType == "map[string]interface{}" {tempParamStr = "Param"mapDeepCopy = fmt.Sprintf(`Param := utils.MapDeepCopy(%s)`, paramImportPath)}// fmt.Println("---------------->", paramType)file.WriteString(fmt.Sprintf("package %s_test\n\n", serverName))file.WriteString(`import . "github.com/onsi/ginkgo"`)file.WriteString("\n\n")file.WriteString(fmt.Sprintf(`var _ = Describe("%s", func() {headers := common.EntireHeaderParamassert := assert.New(GinkgoT())BeforeEach(func() {By("begin test")})JustBeforeEach(func() {By("just say start")})AfterEach(func() {By("end test")})Context("%s", func() {It("正常%s", func() {%sret, resp, _ := %s(%s, headers)assert.Equal(ret.StatusCode, 200)js, errs := simplejson.NewJson(resp)if errs != nil {panic(errs)}%s})})})`, serverName, InterfaceName, InterfaceName, mapDeepCopy, interfaceImportPath, tempParamStr, responseAssertStr))
}func writeParam(file *os.File, serverName string, params []string) {file.WriteString(fmt.Sprintf("package %s", serverName))file.WriteString("\n\n\n")file.WriteString("var (")for _, param := range params {file.WriteString(param)}file.WriteString(")")file.WriteString("\n\n\n")
}func writeReq(file *os.File, InterfaceName, path, method string) {file.WriteString(fmt.Sprintf(`func %s(param %s, header map[string]string) (ret gorequest.Response, content []byte, result string) {path := "%s"url := CN_bbs.TESTSERVERDOMAIN + pathret, content = common.Common%s(url, param, header)fmt.Println(ret.Request.URL)// js, _ := simplejson.NewJson([]byte(content))//result, _ = js.Get("result").String()return}`, InterfaceName, paramType, path, method))
}func genGetParam(interfaceName string, param map[string]interface{}) (formatParam string) {// 对于请求参数的value值为 数组if len(param) > 0 {for k, v := range param {switch vv := v.(type) {case []interface{}:fmt.Sprintf(k, "is an array:", vv)temp, _ := json.Marshal(param)// 如果是数组格式,直接当作字符串处理(map[]interface{}格式无法表示该类型参数)formatParam = fmt.Sprintf("%sParam = `%s`", interfaceName, fmt.Sprintf("%v", string(temp)))returndefault:// fmt.Println(k, "is of a type didn't handle")}}}temp, _ := json.Marshal(param)formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, fmt.Sprintf("%v", string(temp)))return
}func genPostParam(interfaceName string, postReqParamStr string) (formatParam string) {// formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, param)// fmt.Sprintf("%v", string(temp))postReqParam := make(map[string]interface{}, 1)if len(postReqParamStr) > 0 {// 判断第一个字符是否为{}, 做传递数据为数组[]的兼容if []rune(postReqParamStr)[0] == '{' {var x interface{}err := json.Unmarshal([]byte(postReqParamStr), &x)if err != nil {fmt.Println("err", err)}postReqParam = x.(map[string]interface{})// fmt.Println(postReqParam)// 判断value中是否存在数组for k, v := range postReqParam {switch vv := v.(type) {// switch vv := v.(type) {case []interface{}:fmt.Sprintf(k, "is an array:", vv)// param[k] = fmt.Sprintf("`%s`", vv)temp, _ := json.Marshal(postReqParam)formatParam = fmt.Sprintf("%sParam = `%s`", interfaceName, fmt.Sprintf("%v", string(temp)))paramType = "string"returndefault:formatParam = genGetParam(interfaceName, postReqParam)// fmt.Println(k, "is of a type didn't handle")}}// 如果为数组,做如下处理} else {var y []interface{}err := json.Unmarshal([]byte(postReqParamStr), &y)if err != nil {fmt.Println("err", err)}postReqParam = y[0].(map[string]interface{})temp, _ := json.Marshal(postReqParam)// 声明请求类型paramType = "[]map[string]interface{}"formatParam = fmt.Sprintf(`%sParam =[]map[string]interface{}{%s}`, interfaceName, string(temp))// 无法使用 判断类型 Param := utils.MapDeepCopy(Hebinz123.CNppcPlaylistApiV1RemarkDelParam)}}// temp, _ := json.Marshal(param)// formatParam = fmt.Sprintf(`%sParam = map[string]interface{} %s`, interfaceName, fmt.Sprintf("%v", string(temp)))return
}func formatInterfaceName(path string) (InterfaceName string) {paths := strings.Split(path, "/")for k, v := range paths {paths[k] = Capitalize(v)}InterfaceName = strings.Join(paths, "")return
}// Capitalize 字符首字母大写
func Capitalize(str string) string {var upperStr stringvv := []rune(str)for i := 0; i < len(vv); i++ {if i == 0 {if vv[i] >= 97 && vv[i] <= 122 { // 判断是否是小写字母vv[i] -= 32 // string的码表相差32位upperStr += string(vv[i])} else {fmt.Println("Not begins with lowercase letter,")return str}} else {upperStr += string(vv[i])}}return upperStr
}// Har Logs 解析
type Har struct {Log struct {version stringcreator stringEntries []struct {startedDateTime stringtime stringRequest struct {Method stringURL stringhttpVersion stringCookies []stringHeaders []struct {Name stringValue string}QueryString []struct {Name stringValue string}PostData struct {MimeType stringText string}headersSize int32bodySize int32}Response struct {_charlesStatus stringStatus int32StatusText stringhttpVersion stringcookies []stringHeaders []struct {Name stringValue string}Content struct {size int32mimeType stringText stringEncoding string}redirectURL stringheadersSize intbodySize int}serverIPAddress stringcache map[string]stringtimings map[string]int32}}
}// UnpackHar 解析 har
func UnpackHar(har []byte) (logs *Har) {err := json.Unmarshal(har, &logs)if err != nil {fmt.Println(err)}return
}
文中可能存在描述不正确,欢迎大神们指正补充!
感谢阅读,如果觉得对你有帮助,就在右下角点个赞吧,感谢!
合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。
资料获取方法
【留言777】
各位想获取源码等教程资料的朋友请点赞 + 评论 + 收藏,三连!
三连之后我会在评论区挨个私信发给你们~
相关文章:

【Golang】基于录制,自动生成go test接口自动化用例
目录 背景 框架 ginkgo初始化 抓包&运行脚本 目录说明 ∮./business ∮./conf ∮./utils ∮./testcase testcase 用例目录结构规则 示例 实现思路 解析Har数据 定义结构体 解析到json 转换请求数据 转换请求 转换请求参数 写业务请求数据 写gotest测试…...
使用快捷键在Unity中快速锁定和解锁Inspector右上角的锁功能
使用快捷键在Unity中快速锁定和解锁Inspector右上角的锁功能 在Unity中,Inspector窗口是一个非常重要的工具,它允许我们查看和编辑选定对象的属性。有时候,我们可能希望锁定Inspector窗口,以防止意外更改对象的属性。幸运的是&am…...

服务器硬件、部署LNMP动态网站、部署wordpress、配置web与数据库服务分离、配置额外的web服务器
day01 day01项目实战目标单机安装基于LNMP结构的WordPress网站基本环境准备配置nginx配置数据库服务部署wordpressweb与数据库服务分离准备数据库服务器迁移数据库配置额外的web服务器 项目实战目标 主机名IP地址client01192.168.88.10/24web1192.168.88.11/24web2192.168.88…...

面试总被问高并发负载测试,你真的会么?
本文将介绍使用50K并发用户测试轻松运行负载测试所需的步骤(以及最多200万用户的更大测试)。 ❶ 写你的剧本 ❷ 使用JMeter在本地测试 ❸ BlazeMeter SandBox测试 ❹ 使用一个控制台和一个引擎设置每引擎用户数量 ❺ 设置和测试群集(一个…...

ARP协议请求
文章目录 作用请求与应答流程数据包ARP协议以太网帧协议具体应用 作用 通过 IP地址 查找 MAC地址。 请求与应答流程 A:数据发送主机 B:目标主机 目前只知道目标主机IP地址,想把数据发送过去,需要查询到目标主机的MAC地址&#x…...

前端小练-仿掘金导航栏
文章目录 前言项目结构导航实现创作中心移动小球消息提示 完整代码 前言 闲的,你信嘛,还得开发一个基本的门户社区网站,来给到Hlang,不然我怕说工作量不够。那么这个的话,其实也很好办,主要是这个门户网站的UI写起来麻…...
PDF.js实现搜索关键词高亮显示效果
在static\PDF\web\viewer.js找到定义setInitialView方法 大约是在1202行,不同的pdf.js版本不同 在方法体最后面添加如下代码: // 高亮显示关键词---------------------------------------- var keyword new URL(decodeURIComponent(location)).searchP…...

Linux服务器安装JDK20
一、下载安装包 访问官网,找到JDK20,复制下载链接 我复制的链接是:JDK20 二、Linux服务器操作 1.服务器根目录下创建一个新的文件夹 cd /mkdir jdkscd /jdks2.将下载好的jdk-20上传到jdks下 3.解压缩 tar -zxvf jdk-20_linux-x64_bin.tar…...

vue强制刷新的方法
前言 在开发过程中,有时候会遇到这么一种情况: 1.切换页面页面没有更新 2.通过动态的赋值,但是dom没有及时更新,能够获取到动态赋的值,但是无法获取到双向绑定的dom节点, 这就需要我们手动进行强制刷新组件,下面这篇文章主要给大家介绍了关于vue组件强制刷新的方案…...

Linux下TCP网络服务器与客户端通信程序入门
文章目录 目标服务器与客户端通信流程TCP服务器代码TCP客户端代码 目标 实现客户端连接服务器,通过终端窗口发送信息给服务器端,服务器接收到信息后对信息数据进行回传,客户端读取回传信息并返回。 服务器与客户端通信流程 TCP服务器代码 …...
第九章:SSM整合
第九章:SSM整合 9.1:ContextLoaderListener Spring提供了监听器ContextLoaderListener,实现ServletContextListener接口,可监听ServletContext的状态,在web服务器的启动,读取Spring的配置文件…...
shell脚本部署springboot
#!/bin/bashecho "$1 jar包名称,$2 运行环境 " echo "reload jar: $1 env: $2 " if [ -z $1 ];thenecho "请输入jar包名称......." elseecho "停止开始......."IDps -ef | grep "$1" | grep -v "grep"…...
每日一道面试题之Iterator 和 ListIterator 有什么区别?
Iterator 和 ListIterator 都是 Java 集合框架中用于遍历集合元素的接口,但它们有一些区别: 使用的范围:Iterator可以迭代所有集合,而ListIterator 只能用于List及其子类。 继承关系:ListIterator 继承 Iterator,并且ListIterat…...

基于图像形态学处理的停车位检测matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. 图像预处理 4.2. 车辆定位 4.3. 停车位检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ......................................…...
【网络编程】同步IO/异步IO
同步IO的特点: 同步IO指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪。 同步IO的执行者是IO操作的发起者。 同步IO需要发起者进行内核态到用户态的数据拷贝过程,所以这里必须阻塞 异步IO的特点: 异步IO是指用户进程触发I/O…...

五分钟理解NIO与BIO
java NIO与BIO的区别? BIO -- Blocking IO 即阻塞式 IO。NIO -- Non-Blocking IO, 即非阻塞式 IO 或异步 IO。 BIO 基于字节流和字符流进行操作,数据的读取写入必须阻塞在一个线程内等待其完成。 NIO 主要有三大核心部分: Channel (通道)…...

Python数据可视化工具——Pyecharts
目录 1 简介绘图前先导包 2 折线图3 饼图4 柱状图/条形图5 散点图6 箱线图7 热力图8 漏斗图9 3D柱状图10 其他:配置项 1 简介 Pyecharts是一款将python与echarts结合的强大的数据可视化工具 Pyecharts是一个用于生成echarts图表的类库。echarts是百度开源的一个数据…...
cjson常用API使用总结
json JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛用于前后端数据传输和存储。在C语言中,我们可以使用cjson库来处理JSON数据。本问总结了在使用cjson库中各个常用API的用法,包括组装JSON&#x…...
Shell脚本学习-case语句开发rsync服务的脚本
利用case语句开发类似系统启动rsync启动服务的脚本。(可以参考系统rpcbind、nfs的脚本)。 例如: /etc/init.d/rsyncd {start | stop | restart } rsync --daemon pkill rsync [rootvm1 scripts]# cat start_rsync.sh #!/bin/bash #[ -f /…...

使用docker部署一个jar项目
简介: 通过docker镜像, docker可以在服务器上运行包含项目所需运行环境的docker容器, 在线仓库里有很多各个软件公司官方发布的镜像, 或者第三方的镜像. 如果我们需要使用docker把我们的应用程序打包成镜像, 别的机器上只要安装了docker, 就可以直接运行镜像, 而不需要再安装应…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...

GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...

数据分析六部曲?
引言 上一章我们说到了数据分析六部曲,何谓六部曲呢? 其实啊,数据分析没那么难,只要掌握了下面这六个步骤,也就是数据分析六部曲,就算你是个啥都不懂的小白,也能慢慢上手做数据分析啦。 第一…...