grpc.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "html/template"
  6. "io"
  7. "os"
  8. "os/exec"
  9. "path"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "go-common/app/admin/ep/melloi/model"
  14. "go-common/app/admin/ep/melloi/service/proto"
  15. "go-common/library/ecode"
  16. "go-common/library/log"
  17. )
  18. const (
  19. _upCase = 1
  20. _downCase = 0
  21. )
  22. // capitalize 首字母大小写处理
  23. func (s *Service) capitalize(str string, tp int) string {
  24. b := []rune(str)
  25. if tp == _upCase {
  26. if b[0] >= 97 && b[1] <= 122 {
  27. b[0] -= 32
  28. }
  29. } else {
  30. if b[0] >= 64 && b[0] <= 90 {
  31. b[0] += 32
  32. }
  33. }
  34. return string(b)
  35. }
  36. // ProtoParsing analyze proto file
  37. func (s *Service) ProtoParsing(protoPath, protoName string) (res map[string]interface{}, err error) {
  38. var pkgRetMap []string
  39. res = make(map[string]interface{})
  40. reader, err := os.Open(path.Join(protoPath, protoName))
  41. if err != nil {
  42. log.Error("open proto file error(%v)", err)
  43. return nil, err
  44. }
  45. defer reader.Close()
  46. parser := proto.NewParser(reader)
  47. var proto *proto.Proto
  48. proto, err = parser.Parse()
  49. if err != nil {
  50. log.Error("parse proto file error(%v)", err)
  51. return nil, err
  52. }
  53. res["fileName"] = protoName
  54. proto.Filename = strings.TrimSuffix(protoName, ".proto")
  55. res["protoClass"] = s.capitalize(proto.Filename, _upCase)
  56. if len(proto.Imports) > 0 {
  57. var retImportMap []string
  58. for _, impt := range proto.Imports {
  59. retImportMap = append(retImportMap, impt.Filename)
  60. }
  61. res["import"] = retImportMap
  62. }
  63. if len(proto.Package) > 0 {
  64. for _, pkg := range proto.Package {
  65. pkgRetMap = append(pkgRetMap, pkg.Name)
  66. }
  67. res["package"] = pkgRetMap
  68. }
  69. if len(proto.Options) > 0 {
  70. var retOptionsList []string
  71. for _, opt := range proto.Options {
  72. retOptionsList = append(retOptionsList, opt.Name)
  73. // java_package proto
  74. if opt.Name == "java_package" && len(proto.Package) <= 0 {
  75. pkgRetMap = append(pkgRetMap, opt.Constant.Source)
  76. }
  77. }
  78. res["package"] = pkgRetMap
  79. res["options"] = retOptionsList
  80. }
  81. if len(proto.Services) > 0 {
  82. var retImportMap []map[string]interface{}
  83. for _, srv := range proto.Services {
  84. firstTmp := make(map[string]interface{})
  85. firstTmp["name"] = srv.Name
  86. var secondMapTmp []map[string]interface{}
  87. for _, rpc := range srv.RPCElements {
  88. secondTmp := make(map[string]interface{})
  89. method := s.capitalize(rpc.Name, _downCase)
  90. // transfer get_by_uid to getByUid
  91. if strings.ContainsAny(method, "_") {
  92. var nmethod string
  93. methodStrs := strings.Split(method, "_")
  94. for i, item := range methodStrs {
  95. if i != 0 {
  96. item = s.capitalize(item, _upCase)
  97. }
  98. nmethod += item
  99. }
  100. method = nmethod
  101. }
  102. secondTmp["method"] = method
  103. secondTmp["requestType"] = rpc.RequestType
  104. secondTmp["returnType"] = rpc.ReturnsType
  105. secondMapTmp = append(secondMapTmp, secondTmp)
  106. }
  107. firstTmp["rpc"] = secondMapTmp
  108. retImportMap = append(retImportMap, firstTmp)
  109. }
  110. res["service"] = retImportMap
  111. }
  112. return
  113. }
  114. // CreateProtoImportDir create import dir
  115. func (s *Service) CreateProtoImportDir(pathModel *model.ProtoPathModel) (err error) {
  116. cMakeDir := fmt.Sprintf("mkdir -p %s ", path.Join(pathModel.RootPath, pathModel.ExtraPath))
  117. if err = exec.Command("/bin/bash", "-c", cMakeDir).Run(); err != nil {
  118. log.Error("create proto import dir error(%v)", err)
  119. }
  120. return
  121. }
  122. //GRPCQuickStart grpc quickstart
  123. func (s *Service) GRPCQuickStart(c context.Context, request *model.GRPCQuickStartRequest, runUser string, cookies string) (ret map[string]string, err error) {
  124. var (
  125. g *model.GRPC
  126. reportID int
  127. )
  128. addScriptReq := request.GRPCAddScriptRequest
  129. ret = make(map[string]string)
  130. if g, err = s.GRPCAddScript(c, &addScriptReq); err != nil {
  131. log.Error("Save grpc script failed, (%v)", err)
  132. return
  133. }
  134. if reportID, err = s.GRPCRunByModel(c, g, runUser, cookies); err != nil {
  135. log.Error("performance test execution failed, (%v)", err)
  136. return
  137. }
  138. ret["report_id"] = strconv.Itoa(reportID)
  139. return
  140. }
  141. // SaveGRPCQuickStart save gRPC script of quick start
  142. func (s *Service) SaveGRPCQuickStart(c context.Context, request *model.GRPCQuickStartRequest) (err error) {
  143. addScriptReq := request.GRPCAddScriptRequest
  144. _, err = s.GRPCAddScript(c, &addScriptReq)
  145. return err
  146. }
  147. //GRPCAddScript create grpc script
  148. func (s *Service) GRPCAddScript(c context.Context, request *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
  149. // 若是debug ,则执行固定次数
  150. if request.IsDebug == 1 {
  151. request.Loops = 4
  152. request.ThreadsSum = 1
  153. request.TaskName += "_perf_debug" // debug tag
  154. }
  155. if g, err = s.CreateJmx(c, request); err != nil {
  156. log.Error("create jmeter file error (%v)", err)
  157. return
  158. }
  159. g.IsDebug = request.IsDebug
  160. return s.dao.CreateGRPC(g)
  161. }
  162. // CreateJmx create jmx
  163. func (s *Service) CreateJmx(c context.Context, request *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
  164. if g, err = s.createJmeterFile(request); err != nil {
  165. log.Error("create jmeter file error (%v)", err)
  166. return
  167. }
  168. return
  169. }
  170. //GRPCRunByScriptID grpc execution by id
  171. func (s *Service) GRPCRunByScriptID(c context.Context, request *model.GRPCExecuteScriptRequest, runUser string, cookie string) (reportId int, err error) {
  172. var grpc *model.GRPC
  173. if grpc, err = s.dao.QueryGRPCByID(request.ScriptID); err != nil {
  174. return
  175. }
  176. return s.GRPCRunByModel(c, grpc, runUser, cookie)
  177. }
  178. //GRPCRunByModel execute grpc by model data
  179. func (s *Service) GRPCRunByModel(c context.Context, grpc *model.GRPC, runUser string, cookie string) (reportId int, err error) {
  180. var resp model.DoPtestResp
  181. tim := strconv.FormatInt(time.Now().Unix(), 10)
  182. testNameNick := grpc.TaskName + tim
  183. log.Info("开始调用压测grpc job-------\n")
  184. ptestParam := model.DoPtestParam{
  185. UserName: runUser, // 用户名
  186. LoadTime: grpc.LoadTime, //运行时间
  187. TestNames: StringToSlice(grpc.TaskName), //接口名转数组
  188. FileName: grpc.JmxPath, // jmx文件
  189. ResJtl: grpc.JtlLog, // jtl时间戳
  190. JmeterLog: grpc.JmxLog, // jmeterlog时间戳
  191. Department: grpc.Department,
  192. Project: grpc.Project,
  193. IsDebug: false,
  194. APP: grpc.APP,
  195. ScriptID: grpc.ID,
  196. Cookie: cookie, // 用不到
  197. URL: grpc.ServiceName, // 用于微信通知
  198. LabelIDs: nil,
  199. Domain: grpc.HostName, // 微信通知Domain
  200. FileSplit: false, // 文件切割
  201. SplitNum: 0, // 切割数量
  202. JarPath: grpc.JarPath,
  203. Type: model.PROTOCOL_GRPC, //grpc
  204. }
  205. if grpc.IsDebug == 1 {
  206. ptestParam.IsDebug = true
  207. }
  208. if resp, err = s.DoPtestByJmeter(c, ptestParam, StringToSlice(testNameNick)); err != nil {
  209. log.Error("s.DoPtestByJmeter err :(%v)", err)
  210. return
  211. }
  212. reportId = resp.ReportSuID
  213. return
  214. }
  215. //QueryGrpc query grpc list
  216. func (s *Service) QueryGrpc(c context.Context, sessionID string, qgr *model.QueryGRPCRequest) (res *model.QueryGRPCResponse, err error) {
  217. // 获取服务树节点
  218. var (
  219. treeNodes []string
  220. treeNodesd []string
  221. )
  222. if treeNodesd, err = s.QueryUserRoleNode(c, sessionID); err != nil {
  223. log.Error("QueryUserRoleNode err (%v):", err)
  224. return
  225. }
  226. treeNodes = append(treeNodesd, "")
  227. if ExistsInSlice(qgr.Executor, s.c.Melloi.Executor) {
  228. return s.dao.QueryGRPCByWhiteName(&qgr.GRPC, qgr.PageNum, qgr.PageSize)
  229. }
  230. return s.dao.QueryGRPC(&qgr.GRPC, qgr.PageNum, qgr.PageSize, treeNodes)
  231. }
  232. //QueryGrpcById query grpc by id
  233. func (s *Service) QueryGrpcById(id int) (*model.GRPC, error) {
  234. return s.dao.QueryGRPCByID(id)
  235. }
  236. // UpdateGrpc update grpc
  237. func (s *Service) UpdateGrpc(grpc *model.GRPC) error {
  238. return s.dao.UpdateGRPC(grpc)
  239. }
  240. // DeleteGrpc delete grpc by id
  241. func (s *Service) DeleteGrpc(id int) error {
  242. return s.dao.DeleteGRPC(id)
  243. }
  244. //createJmx create jmx file, jtl file, jmx_log file
  245. func (s *Service) createJmeterFile(grpcReq *model.GRPCAddScriptRequest) (g *model.GRPC, err error) {
  246. var (
  247. buff *template.Template
  248. file *os.File
  249. )
  250. if buff, err = template.ParseFiles(s.c.Jmeter.GRPCTemplatePath); err != nil {
  251. log.Error("open grpc.jmx failed! (%v)", err)
  252. return nil, ecode.MelloiJmeterGenerateErr
  253. }
  254. g = model.GRPCReqToGRPC(grpcReq)
  255. // 脚本执行限制
  256. if g.LoadTime > s.c.Jmeter.TestTimeLimit {
  257. g.LoadTime = s.c.Jmeter.TestTimeLimit
  258. }
  259. if g.Loops <= 0 {
  260. g.Loops = -1
  261. }
  262. if g.IsAsync {
  263. g.AsyncInfo = unescaped(model.AsyncInfo)
  264. }
  265. // 生成jmx文件
  266. if grpcReq.ScriptPath == "" {
  267. log.Error("proto file is not uploaded.")
  268. return nil, ecode.MelloiProtoFileNotUploaded
  269. }
  270. g.JmxPath = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".jmx")
  271. g.JmxLog = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".log") // 定义好即可,运行时生成
  272. g.JtlLog = path.Join(grpcReq.ScriptPath, grpcReq.TaskName+".jtl") // 定义好即可,运行时生成
  273. if g.ParamFilePath != "" {
  274. g.ParamEnable = "true"
  275. }
  276. if file, err = os.Create(g.JmxPath); err != nil {
  277. log.Error("create jmx file error :(%v)", err)
  278. return nil, ecode.MelloiJmeterGenerateErr
  279. }
  280. defer file.Close()
  281. // 写入模板数据
  282. buff = buff.Funcs(template.FuncMap{"unescaped": unescaped})
  283. if err = buff.Execute(io.Writer(file), g); err != nil {
  284. log.Error("write jmeter file failed, (%v)", err)
  285. return nil, ecode.MelloiJmeterGenerateErr
  286. }
  287. return
  288. }