From 2fa7e56713df596d1392499e08570c0ea6757e8f Mon Sep 17 00:00:00 2001 From: anqiansong Date: Sat, 2 Mar 2024 02:17:04 +0000 Subject: [PATCH] update document --- cn/about-us.html | 4 +- cn/api-coding.html | 4 +- cn/api-config.html | 4 +- cn/api-dir.html | 4 +- cn/api-grammar.html | 4 +- cn/bloom.html | 4 +- cn/breaker-algorithms.html | 4 +- cn/buiness-cache.html | 4 +- cn/business-coding.html | 4 +- cn/business-dev.html | 4 +- cn/ci-cd.html | 4 +- cn/coding-spec.html | 4 +- cn/collection.html | 4 +- cn/concept-introduction.html | 4 +- cn/config-introduction.html | 4 +- cn/contributor.html | 311 ++++++++++++++++---------------- cn/datacenter.html | 4 +- cn/dev-flow.html | 4 +- cn/dev-specification.html | 4 +- cn/distributed-transaction.html | 4 +- cn/doc-contibute.html | 4 +- cn/eco.html | 4 +- cn/error-handle.html | 4 +- cn/error.html | 4 +- cn/executors.html | 4 +- cn/extended-reading.html | 4 +- cn/faq.html | 4 +- cn/framework-design.html | 4 +- cn/fx.html | 4 +- cn/go-queue.html | 4 +- cn/go-zero-design.html | 4 +- cn/go-zero-features.html | 4 +- cn/go-zero-looklook.html | 4 +- cn/goctl-api.html | 4 +- cn/goctl-commands.html | 4 +- cn/goctl-completion.html | 4 +- cn/goctl-install.html | 4 +- cn/goctl-model.html | 4 +- cn/goctl-other.html | 4 +- cn/goctl-plugin.html | 4 +- cn/goctl-rpc.html | 4 +- cn/goctl.html | 4 +- cn/golang-install.html | 4 +- cn/gomod-config.html | 4 +- cn/goreading.html | 4 +- cn/gotalk.html | 4 +- cn/index.html | 4 +- cn/intellij.html | 4 +- cn/join-us.html | 4 +- cn/jwt.html | 4 +- cn/keywords.html | 4 +- cn/learning-resource.html | 4 +- cn/loadshedding.html | 4 +- cn/log-collection.html | 4 +- cn/logx.html | 4 +- cn/mapping.html | 4 +- cn/mapreduce.html | 4 +- cn/metric.html | 4 +- cn/micro-service.html | 4 +- cn/middleware.html | 4 +- cn/model-gen.html | 4 +- cn/monolithic-service.html | 4 +- cn/mysql.html | 4 +- cn/naming-spec.html | 4 +- cn/online-exchange.html | 4 +- cn/periodlimit.html | 4 +- cn/plugin-center.html | 4 +- cn/prepare-other.html | 4 +- cn/prepare.html | 4 +- cn/project-dev.html | 4 +- cn/protoc-install.html | 4 +- cn/quick-start.html | 4 +- cn/redis-cache.html | 4 +- cn/redis-lock.html | 4 +- cn/route-naming-spec.html | 4 +- cn/rpc-call.html | 4 +- cn/rpc-config.html | 4 +- cn/rpc-dir.html | 4 +- cn/search_plus_index.json | 2 +- cn/service-deployment.html | 4 +- cn/service-design.html | 4 +- cn/service-monitor.html | 4 +- cn/sharedcalls.html | 4 +- cn/shorturl.html | 4 +- cn/source.html | 4 +- cn/sql-cache.html | 4 +- cn/stream.html | 4 +- cn/template-cmd.html | 4 +- cn/template-manage.html | 4 +- cn/template.html | 4 +- cn/timing-wheel.html | 4 +- cn/tips.html | 4 +- cn/tokenlimit.html | 4 +- cn/tool-center.html | 4 +- cn/trace.html | 4 +- cn/vscode.html | 4 +- cn/wechat.html | 4 +- cn/zrpc.html | 4 +- en/about-us.html | 4 +- en/api-coding.html | 4 +- en/api-config.html | 4 +- en/api-dir.html | 4 +- en/api-grammar.html | 4 +- en/bloom.html | 4 +- en/buiness-cache.html | 4 +- en/business-coding.html | 4 +- en/business-dev.html | 4 +- en/ci-cd.html | 4 +- en/coding-spec.html | 4 +- en/concept-introduction.html | 4 +- en/config-introduction.html | 4 +- en/contributor.html | 311 ++++++++++++++++---------------- en/datacenter.html | 4 +- en/dev-flow.html | 4 +- en/dev-specification.html | 4 +- en/distributed-transaction.html | 4 +- en/doc-contibute.html | 4 +- en/eco.html | 4 +- en/error-handle.html | 4 +- en/error.html | 4 +- en/executors.html | 4 +- en/extended-reading.html | 4 +- en/framework-design.html | 4 +- en/fx.html | 4 +- en/go-queue.html | 4 +- en/go-zero-design.html | 4 +- en/go-zero-features.html | 4 +- en/goctl-api.html | 4 +- en/goctl-commands.html | 4 +- en/goctl-completion.html | 4 +- en/goctl-install.html | 4 +- en/goctl-model.html | 4 +- en/goctl-other.html | 4 +- en/goctl-plugin.html | 4 +- en/goctl-rpc.html | 4 +- en/goctl.html | 4 +- en/golang-install.html | 4 +- en/gomod-config.html | 4 +- en/goreading.html | 4 +- en/gotalk.html | 4 +- en/index.html | 4 +- en/intellij.html | 4 +- en/join-us.html | 4 +- en/jwt.html | 4 +- en/learning-resource.html | 4 +- en/log-collection.html | 4 +- en/logx.html | 4 +- en/micro-service.html | 4 +- en/middleware.html | 4 +- en/model-gen.html | 4 +- en/monolithic-service.html | 4 +- en/mysql.html | 4 +- en/naming-spec.html | 4 +- en/online-exchange.html | 4 +- en/periodlimit.html | 4 +- en/plugin-center.html | 4 +- en/practise.html | 4 +- en/prepare-other.html | 4 +- en/prepare.html | 4 +- en/project-dev.html | 4 +- en/protoc-install.html | 4 +- en/quick-start.html | 4 +- en/redis-cache.html | 4 +- en/redis-lock.html | 4 +- en/route-naming-spec.html | 4 +- en/rpc-call.html | 4 +- en/rpc-config.html | 4 +- en/rpc-dir.html | 4 +- en/search_plus_index.json | 2 +- en/service-deployment.html | 4 +- en/service-design.html | 4 +- en/service-monitor.html | 4 +- en/source.html | 4 +- en/stream.html | 4 +- en/template-cmd.html | 4 +- en/template-manage.html | 4 +- en/template.html | 4 +- en/timing-wheel.html | 4 +- en/tokenlimit.html | 4 +- en/tool-center.html | 4 +- en/trace.html | 4 +- en/vscode.html | 4 +- en/wechat.html | 4 +- search_plus_index.json | 2 +- 184 files changed, 679 insertions(+), 665 deletions(-) diff --git a/cn/about-us.html b/cn/about-us.html index 4ea60a8cb..7190e4f0f 100644 --- a/cn/about-us.html +++ b/cn/about-us.html @@ -1521,7 +1521,7 @@

go-zero社区

go-zero社区群

社区群

@@ -1566,7 +1566,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"关于我们","level":"1.3","depth":1,"next":{"title":"加入我们","level":"1.4","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"previous":{"title":"阅读须知","level":"1.2","depth":1,"path":"tips.md","ref":"tips.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"about-us.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"关于我们","level":"1.3","depth":1,"next":{"title":"加入我们","level":"1.4","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"previous":{"title":"阅读须知","level":"1.2","depth":1,"path":"tips.md","ref":"tips.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"about-us.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/api-coding.html b/cn/api-coding.html index 194175c70..4358f5e09 100644 --- a/cn/api-coding.html +++ b/cn/api-coding.html @@ -1554,7 +1554,7 @@

猜你想看

  • api目录结构介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1599,7 +1599,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"api文件编写","level":"1.8.5.3","depth":3,"next":{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"previous":{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-coding.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"api文件编写","level":"1.8.5.3","depth":3,"next":{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"previous":{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-coding.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/api-config.html b/cn/api-config.html index 93f4f36e4..2c3543636 100644 --- a/cn/api-config.html +++ b/cn/api-config.html @@ -1600,7 +1600,7 @@

    redis.RedisConf

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1645,7 +1645,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"api配置","level":"1.8.4.1","depth":3,"next":{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"previous":{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-config.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"api配置","level":"1.8.4.1","depth":3,"next":{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"previous":{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-config.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/api-dir.html b/cn/api-dir.html index 3233f3b22..a80c4b2aa 100644 --- a/cn/api-dir.html +++ b/cn/api-dir.html @@ -1532,7 +1532,7 @@

    api目录介绍 └── types.go
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1577,7 +1577,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"api目录结构","level":"1.7.4","depth":2,"next":{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"previous":{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-dir.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"api目录结构","level":"1.7.4","depth":2,"next":{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"previous":{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-dir.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/api-grammar.html b/cn/api-grammar.html index ba69d6b8d..83da70bd8 100644 --- a/cn/api-grammar.html +++ b/cn/api-grammar.html @@ -2102,7 +2102,7 @@

    Doc&Comment

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -2147,7 +2147,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"api语法介绍","level":"1.7.3","depth":2,"next":{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"previous":{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-grammar.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"api语法介绍","level":"1.7.3","depth":2,"next":{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"previous":{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-grammar.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/bloom.html b/cn/bloom.html index 66573c661..106740454 100644 --- a/cn/bloom.html +++ b/cn/bloom.html @@ -1574,7 +1574,7 @@

    布隆过&

    本节主要介绍了go-zero框架中的 core.bloom 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1619,7 +1619,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"布隆过滤器","level":"1.11.3","depth":2,"next":{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"previous":{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"bloom.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"布隆过滤器","level":"1.11.3","depth":2,"next":{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"previous":{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"bloom.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/breaker-algorithms.html b/cn/breaker-algorithms.html index 6bdf196d0..26c0176a4 100644 --- a/cn/breaker-algorithms.html +++ b/cn/breaker-algorithms.html @@ -1616,7 +1616,7 @@

    熔断器ֹ

    总结

    调用端可以通过熔断机制进行自我保护,防止调用下游服务出现异常,或者耗时过长影响调用端的业务逻辑,很多功能完整的微服务框架都会内置熔断器。其实,不仅微服务调用之间需要熔断器,在调用依赖资源的时候,比如mysql、redis等也可以引入熔断器的机制。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1661,7 +1661,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"next":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},"previous":{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"breaker-algorithms.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"next":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},"previous":{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"breaker-algorithms.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/buiness-cache.html b/cn/buiness-cache.html index 21956d2ab..c64274cef 100644 --- a/cn/buiness-cache.html +++ b/cn/buiness-cache.html @@ -1646,7 +1646,7 @@

    总结

    以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务 监控来衡量时候需要引入biz。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1691,7 +1691,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"next":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"previous":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"buiness-cache.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"next":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"previous":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"buiness-cache.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/business-coding.html b/cn/business-coding.html index 51b453560..e1acddca8 100644 --- a/cn/business-coding.html +++ b/cn/business-coding.html @@ -1624,7 +1624,7 @@

    猜你想看

  • api配置介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1669,7 +1669,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"业务编码","level":"1.8.5.4","depth":3,"next":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"previous":{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-coding.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"业务编码","level":"1.8.5.4","depth":3,"next":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"previous":{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-coding.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/business-dev.html b/cn/business-dev.html index 3e6dd9886..8defb412b 100644 --- a/cn/business-dev.html +++ b/cn/business-dev.html @@ -1570,7 +1570,7 @@

    参考ঈ
    INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男');
     
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1615,7 +1615,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"业务开发","level":"1.8.5","depth":2,"next":{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"previous":{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-dev.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"业务开发","level":"1.8.5","depth":2,"next":{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"previous":{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-dev.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/ci-cd.html b/cn/ci-cd.html index 32ee53b20..652919435 100644 --- a/cn/ci-cd.html +++ b/cn/ci-cd.html @@ -1569,7 +1569,7 @@

    参考文档

  • Gitlab CI
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1614,7 +1614,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"CI/CD","level":"1.8.6","depth":2,"next":{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"previous":{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"ci-cd.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"CI/CD","level":"1.8.6","depth":2,"next":{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"previous":{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"ci-cd.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/coding-spec.html b/cn/coding-spec.html index 6b4e77360..5c6f5c073 100644 --- a/cn/coding-spec.html +++ b/cn/coding-spec.html @@ -1557,7 +1557,7 @@

    函数体߱
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1602,7 +1602,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"编码规范","level":"1.8.2.3","depth":3,"next":{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"previous":{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"coding-spec.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"编码规范","level":"1.8.2.3","depth":3,"next":{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"previous":{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"coding-spec.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/collection.html b/cn/collection.html index 4ac5b4671..45dde5070 100644 --- a/cn/collection.html +++ b/cn/collection.html @@ -1602,7 +1602,7 @@

    本文主要介绍了go-zero框架中的 Cache 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1647,7 +1647,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"next":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},"previous":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"collection.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"next":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},"previous":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"collection.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/concept-introduction.html b/cn/concept-introduction.html index 8f5c3691f..5520fc80f 100644 --- a/cn/concept-introduction.html +++ b/cn/concept-introduction.html @@ -1545,7 +1545,7 @@

    参考文档

  • api语法
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1590,7 +1590,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"概念介绍","level":"1.5","depth":1,"next":{"title":"快速开发","level":"1.6","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"previous":{"title":"加入我们","level":"1.4","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"concept-introduction.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"概念介绍","level":"1.5","depth":1,"next":{"title":"快速开发","level":"1.6","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"previous":{"title":"加入我们","level":"1.4","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"concept-introduction.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/config-introduction.html b/cn/config-introduction.html index 1a4fd67f3..21470301e 100644 --- a/cn/config-introduction.html +++ b/cn/config-introduction.html @@ -1516,7 +1516,7 @@

    配置介绍

  • rpc配置
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1561,7 +1561,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"配置介绍","level":"1.8.4","depth":2,"next":{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"previous":{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"config-introduction.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"配置介绍","level":"1.8.4","depth":2,"next":{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"previous":{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"config-introduction.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/contributor.html b/cn/contributor.html index 65fd3b09c..f3e9a5707 100644 --- a/cn/contributor.html +++ b/cn/contributor.html @@ -1621,6 +1621,13 @@

    go-zero参与人 + + @@ -1677,13 +1684,6 @@

    go-zero参与人 - - - - - - + + + + @@ -1965,16 +1965,16 @@

    go-zero参与人 @@ -2000,100 +2000,100 @@

    go-zero参与人 @@ -2105,23 +2105,30 @@

    go-zero参与人 + + @@ -2189,16 +2196,16 @@

    go-zero参与人 @@ -2336,9 +2343,16 @@

    go-zero参与人 + + @@ -2356,6 +2370,13 @@

    go-zero参与人 + + + + - - - - - - - - + + + + - - @@ -3007,6 +3014,13 @@

    go-zero参与人 + + - -
    codeErrorSleep @@ -3373,7 +3380,7 @@

    文档࣒
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:18 +2024-03-02 02:15:51
    @@ -3418,7 +3425,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"贡献人员","level":"1.14","depth":1,"next":{"title":"文档贡献","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"previous":{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"contributor.md","mtime":"2024-03-01T16:09:18.653Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"贡献人员","level":"1.14","depth":1,"next":{"title":"文档贡献","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"previous":{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"contributor.md","mtime":"2024-03-02T02:15:51.738Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); });

    diff --git a/cn/datacenter.html b/cn/datacenter.html index a9ed89c3d..879593ea7 100644 --- a/cn/datacenter.html +++ b/cn/datacenter.html @@ -2399,7 +2399,7 @@

    使用心得

    root:password@/name?parseTime=True&loc=Local&charset=utf8 修改为: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -2444,7 +2444,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"next":{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"previous":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"datacenter.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"next":{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"previous":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"datacenter.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/dev-flow.html b/cn/dev-flow.html index bb4c13c30..a96a13e87 100644 --- a/cn/dev-flow.html +++ b/cn/dev-flow.html @@ -1538,7 +1538,7 @@

    开发工具

  • Goland(推荐)
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1583,7 +1583,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"开发流程","level":"1.8.3","depth":2,"next":{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"previous":{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-flow.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"开发流程","level":"1.8.3","depth":2,"next":{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"previous":{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-flow.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/dev-specification.html b/cn/dev-specification.html index f965e1c9d..cfe8503e4 100644 --- a/cn/dev-specification.html +++ b/cn/dev-specification.html @@ -1533,7 +1533,7 @@

    Productivity࿰

    在go-zero团队中,一直在强调这个话题,开发人员成产力的多少,并不是你写了多少行代码,完成了多少个模块开发,而是我们需要利用各种有效的途径来利用有限的时间完成开发效率最大化,而Goctl的诞生正是为了提高生产力, 因此这个开发原则我是非常认同的。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1578,7 +1578,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"开发规范","level":"1.8.2","depth":2,"next":{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"previous":{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-specification.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"开发规范","level":"1.8.2","depth":2,"next":{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"previous":{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-specification.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/distributed-transaction.html b/cn/distributed-transaction.html index 3321fa16c..eb63caf64 100644 --- a/cn/distributed-transaction.html +++ b/cn/distributed-transaction.html @@ -1536,7 +1536,7 @@

    更多ש

    更多 dtm 的能力和介绍,参见dtm

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1581,7 +1581,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"分布式事务支持","level":"1.12.2","depth":2,"next":{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"previous":{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"distributed-transaction.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"分布式事务支持","level":"1.12.2","depth":2,"next":{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"previous":{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"distributed-transaction.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/doc-contibute.html b/cn/doc-contibute.html index 5ec45d575..9dbb2bafa 100644 --- a/cn/doc-contibute.html +++ b/cn/doc-contibute.html @@ -1562,7 +1562,7 @@

    猜你想看

  • Github Pull request
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1607,7 +1607,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"文档贡献","level":"1.15","depth":1,"next":{"title":"常见错误处理","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"previous":{"title":"贡献人员","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"doc-contibute.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"文档贡献","level":"1.15","depth":1,"next":{"title":"常见错误处理","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"previous":{"title":"贡献人员","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"doc-contibute.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/eco.html b/cn/eco.html index c02601989..11b97e199 100644 --- a/cn/eco.html +++ b/cn/eco.html @@ -1520,7 +1520,7 @@

    go-zero 生态

  • 插件中心
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1565,7 +1565,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero 生态","level":"1.12","depth":1,"next":{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"previous":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"eco.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero 生态","level":"1.12","depth":1,"next":{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"previous":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"eco.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/error-handle.html b/cn/error-handle.html index fce2c9e21..11473821f 100644 --- a/cn/error-handle.html +++ b/cn/error-handle.html @@ -1677,7 +1677,7 @@

    自定义॑
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1722,7 +1722,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"错误处理","level":"1.8.5.8","depth":3,"next":{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"previous":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error-handle.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"错误处理","level":"1.8.5.8","depth":3,"next":{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"previous":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error-handle.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/error.html b/cn/error.html index 80a5f301a..f2461031a 100644 --- a/cn/error.html +++ b/cn/error.html @@ -1544,7 +1544,7 @@

    goctl找不到

    command not found: goctl
     

    请确保goctl已经安装或者goctl是否已经添加到环境变量

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1589,7 +1589,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"常见错误处理","level":"1.16","depth":1,"next":{"title":"FAQ","level":"1.17","depth":1,"path":"faq.md","ref":"faq.md","articles":[]},"previous":{"title":"文档贡献","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"常见错误处理","level":"1.16","depth":1,"next":{"title":"FAQ","level":"1.17","depth":1,"path":"faq.md","ref":"faq.md","articles":[]},"previous":{"title":"文档贡献","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/executors.html b/cn/executors.html index 3f18dc209..05197c38e 100644 --- a/cn/executors.html +++ b/cn/executors.html @@ -1801,7 +1801,7 @@

    总结

    go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1846,7 +1846,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"executors","level":"1.11.4","depth":2,"next":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"previous":{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"executors.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"executors","level":"1.11.4","depth":2,"next":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"previous":{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"executors.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/extended-reading.html b/cn/extended-reading.html index 7151fba90..19513a826 100644 --- a/cn/extended-reading.html +++ b/cn/extended-reading.html @@ -1541,7 +1541,7 @@

    扩展阅读

  • 10月3日线上交流问题汇总
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1586,7 +1586,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"扩展阅读","level":"1.11","depth":1,"next":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},"previous":{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"extended-reading.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"扩展阅读","level":"1.11","depth":1,"next":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},"previous":{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"extended-reading.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/faq.html b/cn/faq.html index f885f0449..70efa579a 100644 --- a/cn/faq.html +++ b/cn/faq.html @@ -1581,7 +1581,7 @@

    常见फ़

    faq会不定期更新大家遇到的问题,也欢迎大家把常见问题通过pr写在这里。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1626,7 +1626,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"FAQ","level":"1.17","depth":1,"next":{"title":"相关源码","level":"1.18","depth":1,"path":"source.md","ref":"source.md","articles":[]},"previous":{"title":"常见错误处理","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"faq.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"FAQ","level":"1.17","depth":1,"next":{"title":"相关源码","level":"1.18","depth":1,"path":"source.md","ref":"source.md","articles":[]},"previous":{"title":"常见错误处理","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"faq.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/framework-design.html b/cn/framework-design.html index fc76a7f59..d19ba019d 100644 --- a/cn/framework-design.html +++ b/cn/framework-design.html @@ -1520,7 +1520,7 @@

    框架设计

  • rpc目录结构
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1565,7 +1565,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"框架设计","level":"1.7","depth":1,"next":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"previous":{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"framework-design.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"框架设计","level":"1.7","depth":1,"next":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"previous":{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"framework-design.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/fx.html b/cn/fx.html index 3d552d6c2..03ea751d1 100644 --- a/cn/fx.html +++ b/cn/fx.html @@ -1843,7 +1843,7 @@

    并发处理

    总结

    本篇文章介绍了流处理的基本概念和gozero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1888,7 +1888,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"next":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"previous":{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"fx.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"next":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"previous":{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"fx.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/go-queue.html b/cn/go-queue.html index 8dfef91e6..6f6716d3a 100644 --- a/cn/go-queue.html +++ b/cn/go-queue.html @@ -1789,7 +1789,7 @@

    常见问উ
  • 因为beanstalk是单点服务,无法保证高可用。dq可以使用多个单点beanstalk服务,互相备份 & 保证高可用。使用redis解决重复消费问题。
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1834,7 +1834,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"next":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},"previous":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-queue.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"next":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},"previous":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-queue.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/go-zero-design.html b/cn/go-zero-design.html index 86120bba9..cf012e65a 100644 --- a/cn/go-zero-design.html +++ b/cn/go-zero-design.html @@ -1522,7 +1522,7 @@

    go-zero设计理
  • 约束做一件事只有一种方式
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1567,7 +1567,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"next":{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"previous":{"title":"框架设计","level":"1.7","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-design.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"next":{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"previous":{"title":"框架设计","level":"1.7","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-design.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/go-zero-features.html b/cn/go-zero-features.html index 8c092ca9c..44ab3282f 100644 --- a/cn/go-zero-features.html +++ b/cn/go-zero-features.html @@ -1529,7 +1529,7 @@

    go-zero特性

    如下图,我们从多个层面保障了整体服务的高可用:

    弹性设计

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1574,7 +1574,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero特点","level":"1.7.2","depth":2,"next":{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"previous":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-features.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero特点","level":"1.7.2","depth":2,"next":{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"previous":{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-features.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/go-zero-looklook.html b/cn/go-zero-looklook.html index 083955ab5..e1b133f7f 100644 --- a/cn/go-zero-looklook.html +++ b/cn/go-zero-looklook.html @@ -1599,7 +1599,7 @@

    部署

    ​ 拉取代码--->ci检测(这里可以省略哈,自己看着办)--->构建镜像(go-zero官方有Dockerfile还有教程,别告诉我不会)-->推送到阿里云镜像服务--->使用kubectl去阿里云k8s拉取镜像(ack、ask都行,ask无法使用daemonset 不能用filebeat)---->ok了

    另外, 如果你想自己基于gitlab、jenkins、harbor去做的话,嗯 自己去找运维弄吧,我之前也写过一个教程,有空在整吧老哥们!!

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1644,7 +1644,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"next":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"previous":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-looklook.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"next":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"previous":{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-looklook.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-api.html b/cn/goctl-api.html index 76070febb..e6c56deb2 100644 --- a/cn/goctl-api.html +++ b/cn/goctl-api.html @@ -1573,7 +1573,7 @@

    猜你想看

  • api目录
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1618,7 +1618,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"api命令","level":"1.9.3","depth":2,"next":{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"previous":{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-api.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"api命令","level":"1.9.3","depth":2,"next":{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"previous":{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-api.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-commands.html b/cn/goctl-commands.html index d2150c5de..5986f911c 100644 --- a/cn/goctl-commands.html +++ b/cn/goctl-commands.html @@ -1781,7 +1781,7 @@

    completion

    (生成自动补全脚本,它只适用于类unix操作系统)

    --name, -n:自动完成脚本的文件名,默认为[goctl_autocomplete]

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1826,7 +1826,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"命令大全","level":"1.9.2","depth":2,"next":{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"previous":{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-commands.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"命令大全","level":"1.9.2","depth":2,"next":{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"previous":{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-commands.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-completion.html b/cn/goctl-completion.html index 511059e8c..938e661db 100644 --- a/cn/goctl-completion.html +++ b/cn/goctl-completion.html @@ -1555,7 +1555,7 @@

    演示效果

    upgrade -- upgrade goctl to latest version
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1600,7 +1600,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"自动补全设置","level":"1.9.1","depth":2,"next":{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"previous":{"title":"Goctl","level":"1.9","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-completion.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"自动补全设置","level":"1.9.1","depth":2,"next":{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"previous":{"title":"Goctl","level":"1.9","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-completion.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-install.html b/cn/goctl-install.html index 9deada4a8..8ad62249b 100644 --- a/cn/goctl-install.html +++ b/cn/goctl-install.html @@ -1547,7 +1547,7 @@

    安装(mac&linux)

    windows用户添加环境变量请自行google

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1592,7 +1592,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"goctl安装","level":"1.8.1.3","depth":3,"next":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"previous":{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-install.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"goctl安装","level":"1.8.1.3","depth":3,"next":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"previous":{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-install.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-model.html b/cn/goctl-model.html index e4585221f..b72afa972 100644 --- a/cn/goctl-model.html +++ b/cn/goctl-model.html @@ -1931,7 +1931,7 @@

    类型ࣶ
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1976,7 +1976,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"model命令","level":"1.9.5","depth":2,"next":{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"previous":{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-model.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"model命令","level":"1.9.5","depth":2,"next":{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"previous":{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-model.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-other.html b/cn/goctl-other.html index 44040a9f5..1f89a4951 100644 --- a/cn/goctl-other.html +++ b/cn/goctl-other.html @@ -1796,7 +1796,7 @@

    猜你想看

  • k8s介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1841,7 +1841,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"其他命令","level":"1.9.7","depth":2,"next":{"title":"模板管理","level":"1.10","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"previous":{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-other.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"其他命令","level":"1.9.7","depth":2,"next":{"title":"模板管理","level":"1.10","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"previous":{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-other.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-plugin.html b/cn/goctl-plugin.html index 52e2ce446..1b0080573 100644 --- a/cn/goctl-plugin.html +++ b/cn/goctl-plugin.html @@ -1565,7 +1565,7 @@

    猜你想看

  • api命令介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1610,7 +1610,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"plugin命令","level":"1.9.6","depth":2,"next":{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"previous":{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-plugin.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"plugin命令","level":"1.9.6","depth":2,"next":{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"previous":{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-plugin.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl-rpc.html b/cn/goctl-rpc.html index 047b403a9..9c78cc144 100644 --- a/cn/goctl-rpc.html +++ b/cn/goctl-rpc.html @@ -1700,7 +1700,7 @@

    猜你想看

  • rpc调用
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1745,7 +1745,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"rpc命令","level":"1.9.4","depth":2,"next":{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"previous":{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-rpc.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"rpc命令","level":"1.9.4","depth":2,"next":{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"previous":{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-rpc.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goctl.html b/cn/goctl.html index ad4fbf179..260017048 100644 --- a/cn/goctl.html +++ b/cn/goctl.html @@ -1564,7 +1564,7 @@

    常见问题

    command not found: goctl
     

    请确保goctl已经安装,或者goctl是否已经正确添加到当前shell的环境变量中。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1609,7 +1609,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Goctl","level":"1.9","depth":1,"next":{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"previous":{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"Goctl","level":"1.9","depth":1,"next":{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"previous":{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/golang-install.html b/cn/golang-install.html index 6559d8679..d9c659802 100644 --- a/cn/golang-install.html +++ b/cn/golang-install.html @@ -1550,7 +1550,7 @@

    Windows安装Go

    其他

    更多操作系统安装见https://golang.org/dl/

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1595,7 +1595,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"golang安装","level":"1.8.1.1","depth":3,"next":{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"previous":{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"golang-install.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"golang安装","level":"1.8.1.1","depth":3,"next":{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"previous":{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"golang-install.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/gomod-config.html b/cn/gomod-config.html index 2a298e8d4..1b34e000c 100644 --- a/cn/gomod-config.html +++ b/cn/gomod-config.html @@ -1540,7 +1540,7 @@

    MODULE配置

    参考文档

    [1] Go Modules Reference

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1585,7 +1585,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go module配置","level":"1.8.1.2","depth":3,"next":{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"previous":{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gomod-config.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go module配置","level":"1.8.1.2","depth":3,"next":{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"previous":{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gomod-config.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/goreading.html b/cn/goreading.html index b4b9e3f3e..aa5f89b33 100644 --- a/cn/goreading.html +++ b/cn/goreading.html @@ -1518,7 +1518,7 @@

    Go夜读

  • 再见go-micro!企业项目迁移go-zero全攻略(一)
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1563,7 +1563,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Go夜读","level":"1.13.2","depth":2,"next":{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"previous":{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goreading.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"Go夜读","level":"1.13.2","depth":2,"next":{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"previous":{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goreading.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/gotalk.html b/cn/gotalk.html index 06b4bd5b4..817bd5d21 100644 --- a/cn/gotalk.html +++ b/cn/gotalk.html @@ -1514,7 +1514,7 @@

    Go开源说

  • Go 开源说第四期 - Go-Zero
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1559,7 +1559,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Go开源说","level":"1.13.3","depth":2,"next":{"title":"贡献人员","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"previous":{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gotalk.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"Go开源说","level":"1.13.3","depth":2,"next":{"title":"贡献人员","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"previous":{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gotalk.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/index.html b/cn/index.html index 10ba6ec3f..dd0525e1d 100644 --- a/cn/index.html +++ b/cn/index.html @@ -1677,7 +1677,7 @@

    9. 微信交&

    加我之前有劳点一下 star,一个小小的 star 是作者们回答海量问题的动力🤝

    wechat

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1718,7 +1718,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"简介","level":"1.1","depth":1,"next":{"title":"阅读须知","level":"1.2","depth":1,"path":"tips.md","ref":"tips.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"README.md","mtime":"2024-03-01T16:09:17.297Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"简介","level":"1.1","depth":1,"next":{"title":"阅读须知","level":"1.2","depth":1,"path":"tips.md","ref":"tips.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"README.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/intellij.html b/cn/intellij.html index 12a113f09..614b3cf5a 100644 --- a/cn/intellij.html +++ b/cn/intellij.html @@ -1682,7 +1682,7 @@

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1727,7 +1727,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"intellij插件","level":"1.12.1.1","depth":3,"next":{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"previous":{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"intellij.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"intellij插件","level":"1.12.1.1","depth":3,"next":{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"previous":{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"intellij.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/join-us.html b/cn/join-us.html index eaf8b662f..40c427c22 100644 --- a/cn/join-us.html +++ b/cn/join-us.html @@ -1562,7 +1562,7 @@

    参考文档

  • Github Pull request
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1607,7 +1607,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"加入我们","level":"1.4","depth":1,"next":{"title":"概念介绍","level":"1.5","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"previous":{"title":"关于我们","level":"1.3","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"join-us.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"加入我们","level":"1.4","depth":1,"next":{"title":"概念介绍","level":"1.5","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"previous":{"title":"关于我们","level":"1.3","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"join-us.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/jwt.html b/cn/jwt.html index 4178c5557..422e97ad9 100644 --- a/cn/jwt.html +++ b/cn/jwt.html @@ -1716,7 +1716,7 @@

    猜你想看

  • api语法
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1761,7 +1761,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"next":{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"previous":{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"jwt.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"next":{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"previous":{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"jwt.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/keywords.html b/cn/keywords.html index b8e7ace94..464d1ebb3 100644 --- a/cn/keywords.html +++ b/cn/keywords.html @@ -1589,7 +1589,7 @@

    5. Benchmark

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1634,7 +1634,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"next":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},"previous":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"keywords.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"next":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},"previous":{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"keywords.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/learning-resource.html b/cn/learning-resource.html index b9cfcbd87..3339d8171 100644 --- a/cn/learning-resource.html +++ b/cn/learning-resource.html @@ -1517,7 +1517,7 @@

    学习资源

  • Go开源说
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1562,7 +1562,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"学习资源","level":"1.13","depth":1,"next":{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"previous":{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"learning-resource.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"学习资源","level":"1.13","depth":1,"next":{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"previous":{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"learning-resource.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/loadshedding.html b/cn/loadshedding.html index ec0e50724..0d97e4759 100644 --- a/cn/loadshedding.html +++ b/cn/loadshedding.html @@ -1580,7 +1580,7 @@

    参考资料

  • Kratos自适应限流保护
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1625,7 +1625,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"next":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},"previous":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"loadshedding.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"next":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},"previous":{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"loadshedding.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/log-collection.html b/cn/log-collection.html index f1c200a3c..afb5b0fe5 100644 --- a/cn/log-collection.html +++ b/cn/log-collection.html @@ -1646,7 +1646,7 @@

    参考文档

  • filebeat配置
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1691,7 +1691,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"日志收集","level":"1.8.8","depth":2,"next":{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"previous":{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"log-collection.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"日志收集","level":"1.8.8","depth":2,"next":{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"previous":{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"log-collection.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/logx.html b/cn/logx.html index f72aa31cc..c01f5ce10 100644 --- a/cn/logx.html +++ b/cn/logx.html @@ -1651,7 +1651,7 @@

    KeepDays

    StackCooldownMillis

    StackCooldownMillis 定义了日志输出间隔,默认为 100 毫秒。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1696,7 +1696,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"日志组件介绍","level":"1.11.2","depth":2,"next":{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"previous":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"logx.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"日志组件介绍","level":"1.11.2","depth":2,"next":{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"previous":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"logx.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/mapping.html b/cn/mapping.html index 7dfafc6e5..884578c04 100644 --- a/cn/mapping.html +++ b/cn/mapping.html @@ -1631,7 +1631,7 @@

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50 @@ -1676,7 +1676,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"next":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},"previous":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mapping.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"next":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},"previous":{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mapping.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/mapreduce.html b/cn/mapreduce.html index e3fa18bbc..81dccf886 100644 --- a/cn/mapreduce.html +++ b/cn/mapreduce.html @@ -1667,7 +1667,7 @@

    并S

    本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1712,7 +1712,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"next":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},"previous":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mapreduce.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"next":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},"previous":{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mapreduce.md","mtime":"2024-03-02T02:15:50.478Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/metric.html b/cn/metric.html index 452588517..71d6ede62 100644 --- a/cn/metric.html +++ b/cn/metric.html @@ -1593,7 +1593,7 @@

    grafana可视

    总结

    以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1638,7 +1638,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"next":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},"previous":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"metric.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"next":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},"previous":{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"metric.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/micro-service.html b/cn/micro-service.html index 005c00417..6636c1339 100644 --- a/cn/micro-service.html +++ b/cn/micro-service.html @@ -1816,7 +1816,7 @@

    猜你想看

  • rpc调用方说明
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1861,7 +1861,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"微服务","level":"1.6.2","depth":2,"next":{"title":"框架设计","level":"1.7","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"previous":{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"micro-service.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"微服务","level":"1.6.2","depth":2,"next":{"title":"框架设计","level":"1.7","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"go-zero设计理念","level":"1.7.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"go-zero特点","level":"1.7.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"api语法介绍","level":"1.7.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"previous":{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"micro-service.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/middleware.html b/cn/middleware.html index f0b695e76..692a1adee 100644 --- a/cn/middleware.html +++ b/cn/middleware.html @@ -1650,7 +1650,7 @@

    https://github.com/zeromicro/zero-examples/tree/main/http/middleware

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1695,7 +1695,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"中间件使用","level":"1.8.5.6","depth":3,"next":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"previous":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"middleware.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"中间件使用","level":"1.8.5.6","depth":3,"next":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"previous":{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"middleware.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/model-gen.html b/cn/model-gen.html index 0a6b406b7..5797e6f45 100644 --- a/cn/model-gen.html +++ b/cn/model-gen.html @@ -1558,7 +1558,7 @@

    更多

    猜你想看

    model命令及其原理

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1603,7 +1603,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"model生成","level":"1.8.5.2","depth":3,"next":{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"previous":{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"model-gen.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"model生成","level":"1.8.5.2","depth":3,"next":{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"previous":{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"model-gen.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/monolithic-service.html b/cn/monolithic-service.html index 7b707005c..3b20834f4 100644 --- a/cn/monolithic-service.html +++ b/cn/monolithic-service.html @@ -1589,7 +1589,7 @@

    猜你想看

  • api中间件使用
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1634,7 +1634,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"单体服务","level":"1.6.1","depth":2,"next":{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"previous":{"title":"快速开发","level":"1.6","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"monolithic-service.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"单体服务","level":"1.6.1","depth":2,"next":{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"previous":{"title":"快速开发","level":"1.6","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"微服务","level":"1.6.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"monolithic-service.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/mysql.html b/cn/mysql.html index 290d15eb4..6a713d9bb 100644 --- a/cn/mysql.html +++ b/cn/mysql.html @@ -1661,7 +1661,7 @@

    事务

    分布式事务

    go-zero 与 dtm 深度合作,原生的支持了分布式事务,详情参见 分布式事务支持

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1706,7 +1706,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"next":{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"previous":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mysql.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"next":{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"previous":{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mysql.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/naming-spec.html b/cn/naming-spec.html index 7823098cf..97542c88c 100644 --- a/cn/naming-spec.html +++ b/cn/naming-spec.html @@ -1565,7 +1565,7 @@

    参考文档

  • Practical Go: Real world advice for writing maintainable Go programs
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1610,7 +1610,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"命名规范","level":"1.8.2.1","depth":3,"next":{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"previous":{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"naming-spec.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"命名规范","level":"1.8.2.1","depth":3,"next":{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"previous":{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"naming-spec.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/online-exchange.html b/cn/online-exchange.html index 2d71b4514..f5c16a86a 100644 --- a/cn/online-exchange.html +++ b/cn/online-exchange.html @@ -1711,7 +1711,7 @@

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50 @@ -1756,7 +1756,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"next":{"title":"go-zero 生态","level":"1.12","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"previous":{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"online-exchange.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"next":{"title":"go-zero 生态","level":"1.12","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"previous":{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"online-exchange.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/periodlimit.html b/cn/periodlimit.html index 5a4e69cfd..7011e9465 100644 --- a/cn/periodlimit.html +++ b/cn/periodlimit.html @@ -1647,7 +1647,7 @@

    参考

  • tokenlimit
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1692,7 +1692,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"periodlimit限流","level":"1.11.8","depth":2,"next":{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"previous":{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"periodlimit.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"periodlimit限流","level":"1.11.8","depth":2,"next":{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"previous":{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"periodlimit.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/plugin-center.html b/cn/plugin-center.html index 3014f756b..f8076301b 100644 --- a/cn/plugin-center.html +++ b/cn/plugin-center.html @@ -1528,7 +1528,7 @@

    猜你想看

  • api语法介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1573,7 +1573,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"插件中心","level":"1.12.3","depth":2,"next":{"title":"学习资源","level":"1.13","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"previous":{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"plugin-center.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"插件中心","level":"1.12.3","depth":2,"next":{"title":"学习资源","level":"1.13","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"previous":{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"plugin-center.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/prepare-other.html b/cn/prepare-other.html index 1eff7ed01..edc54709e 100644 --- a/cn/prepare-other.html +++ b/cn/prepare-other.html @@ -1520,7 +1520,7 @@

    其他环境

  • mysql
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1565,7 +1565,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"其他","level":"1.8.1.5","depth":3,"next":{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"previous":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare-other.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"其他","level":"1.8.1.5","depth":3,"next":{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"previous":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare-other.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/prepare.html b/cn/prepare.html index 8501b070a..6afcecfa5 100644 --- a/cn/prepare.html +++ b/cn/prepare.html @@ -1520,7 +1520,7 @@

    准备工作

  • 其他
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1565,7 +1565,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"准备工作","level":"1.8.1","depth":2,"next":{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"previous":{"title":"项目开发","level":"1.8","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"准备工作","level":"1.8.1","depth":2,"next":{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"previous":{"title":"项目开发","level":"1.8","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/project-dev.html b/cn/project-dev.html index 2e5231732..94b67c73d 100644 --- a/cn/project-dev.html +++ b/cn/project-dev.html @@ -1550,7 +1550,7 @@

    项目开发

  • 服务监控
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1595,7 +1595,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"项目开发","level":"1.8","depth":1,"next":{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"previous":{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"project-dev.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"项目开发","level":"1.8","depth":1,"next":{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"previous":{"title":"rpc目录结构","level":"1.7.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"project-dev.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/protoc-install.html b/cn/protoc-install.html index cf4723579..1f096f904 100644 --- a/cn/protoc-install.html +++ b/cn/protoc-install.html @@ -1571,7 +1571,7 @@

    protoc-gen-go/protoc-g protoc-gen-go安装失败请阅读常见错误处理

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1616,7 +1616,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"next":{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"previous":{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"protoc-install.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"next":{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"previous":{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"protoc-install.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/quick-start.html b/cn/quick-start.html index 0d7966167..f368dd939 100644 --- a/cn/quick-start.html +++ b/cn/quick-start.html @@ -1516,7 +1516,7 @@

    快速开发

  • 微服务
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1561,7 +1561,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"快速开发","level":"1.6","depth":1,"next":{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"previous":{"title":"概念介绍","level":"1.5","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"quick-start.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"快速开发","level":"1.6","depth":1,"next":{"title":"单体服务","level":"1.6.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"previous":{"title":"概念介绍","level":"1.5","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"quick-start.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/redis-cache.html b/cn/redis-cache.html index 8c7b12c92..b4cb4b311 100644 --- a/cn/redis-cache.html +++ b/cn/redis-cache.html @@ -1756,7 +1756,7 @@

    猜你想看

  • Goctl
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1801,7 +1801,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"next":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"previous":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-cache.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"next":{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"previous":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-cache.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/redis-lock.html b/cn/redis-lock.html index 837406cf8..72ad720d1 100644 --- a/cn/redis-lock.html +++ b/cn/redis-lock.html @@ -1631,7 +1631,7 @@

    释放锁

    不能释放别人的锁,不能释放别人的锁,不能释放别人的锁

    所以需要先 get(key) == value「key」,为 true 才会去 delete

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1676,7 +1676,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"redis锁","level":"1.11.7","depth":2,"next":{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"previous":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-lock.md","mtime":"2024-03-01T16:09:17.301Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"redis锁","level":"1.11.7","depth":2,"next":{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"previous":{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-lock.md","mtime":"2024-03-02T02:15:50.482Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/route-naming-spec.html b/cn/route-naming-spec.html index 11cdc6283..b30e8475c 100644 --- a/cn/route-naming-spec.html +++ b/cn/route-naming-spec.html @@ -1520,7 +1520,7 @@

    路由规范

    /user/password/change/:id
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1565,7 +1565,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"路由规范","level":"1.8.2.2","depth":3,"next":{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"previous":{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"route-naming-spec.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"路由规范","level":"1.8.2.2","depth":3,"next":{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"previous":{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"route-naming-spec.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/rpc-call.html b/cn/rpc-call.html index 701563549..ac1cd6f70 100644 --- a/cn/rpc-call.html +++ b/cn/rpc-call.html @@ -1741,7 +1741,7 @@

    猜你想看

  • goctl rpc命令
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1786,7 +1786,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"next":{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"previous":{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-call.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"next":{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"previous":{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-call.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/rpc-config.html b/cn/rpc-config.html index 8c71983e3..f2286320a 100644 --- a/cn/rpc-config.html +++ b/cn/rpc-config.html @@ -1553,7 +1553,7 @@

    redis.RedisKeyConf

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1598,7 +1598,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"rpc配置","level":"1.8.4.2","depth":3,"next":{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"previous":{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-config.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"rpc配置","level":"1.8.4.2","depth":3,"next":{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"previous":{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-config.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/rpc-dir.html b/cn/rpc-dir.html index 4fc80bc88..024d7528d 100644 --- a/cn/rpc-dir.html +++ b/cn/rpc-dir.html @@ -1572,7 +1572,7 @@

    goctl rpc proto

    [1] pb.go & _grpc.pb.go 文件所在目录并非固定,该目录有 go_opt & go-grpc_opt 与 proto文件中的 go_package 值共同决定,想要了解grpc代码生成目录逻辑请阅读 Go Generated Code

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1617,7 +1617,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"rpc目录结构","level":"1.7.5","depth":2,"next":{"title":"项目开发","level":"1.8","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"previous":{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-dir.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"rpc目录结构","level":"1.7.5","depth":2,"next":{"title":"项目开发","level":"1.8","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"准备工作","level":"1.8.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"golang安装","level":"1.8.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"go module配置","level":"1.8.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"goctl安装","level":"1.8.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go安装","level":"1.8.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"其他","level":"1.8.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"开发规范","level":"1.8.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"命名规范","level":"1.8.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"路由规范","level":"1.8.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"编码规范","level":"1.8.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"开发流程","level":"1.8.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"配置介绍","level":"1.8.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"api配置","level":"1.8.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"rpc配置","level":"1.8.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"服务部署","level":"1.8.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"previous":{"title":"api目录结构","level":"1.7.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-dir.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/search_plus_index.json b/cn/search_plus_index.json index b5b062748..7230ce682 100644 --- a/cn/search_plus_index.json +++ b/cn/search_plus_index.json @@ -1 +1 @@ -{"./":{"url":"./","title":"简介","keywords":"","body":" go-zero 缩短从需求到上线的距离 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 使用 go-zero 的好处: 轻松获得支撑千万日活服务的稳定性 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码 微服务治理中间件可无缝集成到其它现有框架使用 极简的 API 描述,一键生成各端代码 自动校验客户端请求参数合法性 大量微服务治理和并发工具包 1. go-zero 框架背景 18 年初,我们决定从 Java+MongoDB 的单体架构迁移到微服务架构,经过仔细思考和对比,我们决定: 基于 Go 语言 高效的性能 简洁的语法 广泛验证的工程效率 极致的部署体验 极低的服务端资源成本 自研微服务框架 有过很多微服务框架自研经验 需要有更快速的问题定位能力 更便捷的增加新特性 2. go-zero 框架设计思考 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 我们经历不到半年时间,彻底完成了从 Java+MongoDB 到 Golang+MySQL 为主的微服务体系迁移,并于 18 年 8 月底完全上线,稳定保障了业务后续迅速增长,确保了整个服务的高可用。 3. go-zero 项目实现和特点 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: 觉得不错的话,别忘 star 👏 4. Installation 在项目目录下通过如下命令安装: GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero 5. Quick Start 完整示例请查看 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 安装 goctl 工具 goctl 读作 go control,不要读成 go C-T-L。goctl 的意思是不要被代码控制,而是要去控制它。其中的 go 不是指 golang。在设计 goctl 之初,我就希望通过 她 来解放我们的双手👈 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 如果使用 go1.16 版本, 可以使用 go install 命令安装 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 确保 goctl 可执行 快速生成 api 服务 goctl api new greet cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: curl -i http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 22 Oct 2020 14:03:18 GMT Content-Length: 14 {\"message\":\"\"} 编写业务代码: api 文件定义了服务对外暴露的路由,可参考 api 规范 可以在 servicecontext.go 里面传递依赖给 logic,比如 mysql, redis 等 在 api 定义的 get/post/put/delete 等请求对应的 logic 里增加业务处理逻辑 可以根据 api 文件生成前端需要的 Java, TypeScript, Dart, JavaScript 代码 goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 6. Benchmark 测试代码见这里 7. 文档 API 文档 goctl 使用帮助 awesome 系列(更多文章见『微服务实践』公众号) 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 精选 goctl 插件 插件 用途 goctl-swagger 一键生成 api 的 swagger 文档 goctl-android 生成 java (android) 端 http client 请求代码 goctl-go-compact 合并 api 里同一个 group 里的 handler 到一个 go 文件 8. 微信公众号 go-zero 相关文章都会在 微服务实践 公众号整理呈现,欢迎扫码关注,也可以通过公众号私信我 👏 9. 微信交流群 如果文档中未能覆盖的任何疑问,欢迎您在群里提出,我们会尽快答复。 您可以在群内提出使用中需要改进的地方,我们会考虑合理性并尽快修改。 如果您发现 bug 请及时提 issue,我们会尽快确认并修改。 为了防止广告用户、识别技术同行,请 star 后加我时注明 github 当前 star 数,我再拉进 go-zero 群,感谢! 加我之前有劳点一下 star,一个小小的 star 是作者们回答海量问题的动力🤝 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"about-us.html":{"url":"about-us.html","title":"关于我们","keywords":"","body":"关于我们 go-zero go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 go-zero作者 万俊峰,七牛云技术副总裁,拥有14年研发团队管理经验,16年架构设计经验,20年工程实战经验,负责过多个大型项目的架构设计,曾多次合伙创业(被收购),阿里云MVP,ArchSummit全球架构师峰会明星讲师,GopherChina大会主持人 & 金牌讲师,QCon+ Go语言出品人兼讲师,腾讯云开发者大会讲师。 go-zero社区 我们目前拥有7000多人的社区成员,在这里,你可以和大家讨论任何关于go-zero的技术,问题反馈,获取最新的go-zero信息,以及各位大佬每天分享的技术心得。 go-zero社区群 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"join-us.html":{"url":"join-us.html","title":"加入我们","keywords":"","body":"加入我们 概要 go-zero 是一个基于MIT License 的开源项目,大家在使用中发现bug,有新的特性等,均可以参与到go-zero的贡献中来,我们非常欢迎大家的积极参与,也会最快响应大家提出的各种问题,pr等。 贡献形式 Pull Request Issue 贡献须知 go-zero 的Pull request中的代码需要满足一定规范 命名规范,请阅读命名规范 以英文注释为主 pr时备注好功能特性,描述需要清晰,简洁 增加单元测试覆盖率达80%+ 贡献代码(pr) 进入go-zero 项目,fork一份go-zero 项目到自己的github仓库中。 回到自己的github主页,找到xx/go-zero项目,其中xx为你的用户名,如anqiansong/go-zero 克隆代码到本地 开发代码,push到自己的github仓库 进入自己的github中go-zero项目,点击浮层上的的【Pull requests】进入Compare页面。 base repository选择zeromicro/go-zero base:master,head repository选择xx/go-zero compare:$branch ,$branch为你开发的分支,如图: 点击【Create pull request】即可实现pr申请 确认pr是否提交成功,进入go-zero 的Pull requests 查看,应该有自己提交的记录,名称为你的开发时的分支名称 Issue 在我们的社区中,有很多伙伴会积极的反馈一些go-zero使用过程中遇到的问题,由于社区人数较多,我们虽然会实时的关注社区动态,但大家问题反馈过来都是随机的,当我们团队还在解决某一个伙伴提出的问题时,另外的问题也反馈上来,可能会导致团队会很容易忽略掉,为了能够一一的解决大家的问题,我们强烈建议大家通过issue的方式来反馈问题,包括但不限于bug,期望的新功能特性等,我们在实现某一个新特性时也会在issue中体现,大家在这里也能够在这里获取到go-zero的最新动向,也欢迎大家来积极的参与讨论。 怎么提Issue 点击这里 进入go-zero的Issue页面或者直接访问https://github.com/zeromicro/go-zero/issues 地址 点击右上角的【New issue】新建issue 填写issue标题和内容 点击【Submit new issue】提交issue 参考文档 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"概念介绍","keywords":"","body":"概念介绍 go-zero 晓黑板golang开源项目,集各种工程实践于一身的web和rpc框架。 goctl 一个旨在为开发人员提高工程效率、降低出错率的辅助工具。 goctl插件 指以goctl为中心的周边二进制资源,能够满足一些个性化的代码生成需求,如路由合并插件goctl-go-compact插件, 生成swagger文档的goctl-swagger插件,生成php调用端的goctl-php插件等。 intellij/vscode插件 在intellij系列产品上配合goctl开发的插件,其将goctl命令行操作使用UI进行替代。 api文件 api文件是指用于定义和描述api服务的文本文件,其以.api后缀结尾,包含api语法描述内容。 goctl环境 goctl环境是使用goctl前的准备环境,包含 golang环境 protoc protoc-gen-go插件 go module | gopath go-zero-demo go-zero-demo里面包含了文档中所有源码的一个大仓库,后续我们在编写演示demo时,我们均在此项目下创建子项目, 因此我们需要提前创建一个大仓库go-zero-demo,我这里把这个仓库放在home目录下。 $ cd ~ $ mkdir go-zero-demo&&cd go-zero-demo $ go mod init go-zero-demo 参考文档 go-zero Goctl 插件中心 工具中心 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"quick-start.html":{"url":"quick-start.html","title":"快速开发","keywords":"","body":"快速开发 本节主要通过对 api/rpc 等服务快速开始来让大家对使用 go-zero 开发的工程有一个宏观概念,更加详细的介绍我们将在后续一一展开。如果您已经参考 准备工作 做好环境及工具的准备,请跟随以下小节开始体验: 单体服务 微服务 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"单体服务","keywords":"","body":"单体服务 前言 由于go-zero集成了web/rpc于一体,社区有部分小伙伴会问我,go-zero的定位是否是一款微服务框架,答案是不止于此, go-zero虽然集众多功能于一身,但你可以将其中任何一个功能独立出来去单独使用,也可以开发单体服务, 不是说每个服务上来就一定要采用微服务的架构的设计,这点大家可以看看作者(kevin)的第四期开源说 ,其中对此有详细的讲解。 创建greet服务 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 查看一下greet服务的目录结构 $ tree greet greet ├── etc │ └── greet-api.yaml ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go 由以上目录结构可以观察到,greet服务虽小,但\"五脏俱全\"。接下来我们就可以在greetlogic.go中编写业务代码了。 编写逻辑 $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req *types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } 启动并访问服务 启动服务 $ cd greet $ go run greet.go -f etc/greet-api.yaml 输出如下,服务启动并侦听在8888端口: Starting server at 0.0.0.0:8888... 访问服务 $ curl -i -X GET http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} 源码 greet源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"micro-service.html":{"url":"micro-service.html","title":"微服务","keywords":"","body":"微服务 在上一篇我们已经演示了怎样快速创建一个单体服务,接下来我们来演示一下如何快速创建微服务, 在本小节中,api部分其实和单体服务的创建逻辑是一样的,只是在单体服务中没有服务间的通讯而已, 且微服务中api服务会多一些rpc调用的配置。 前言 本小节将以一个订单服务调用用户服务来简单演示一下,演示代码仅传递思路,其中有些环节不会一一列举。 情景提要 假设我们在开发一个商城项目,而开发者小明负责用户模块(user)和订单模块(order)的开发,我们姑且将这两个模块拆分成两个微服务① [注意] ①:微服务的拆分也是一门学问,这里我们就不讨论怎么去拆分微服务的细节了。 演示功能目标 订单服务(order)提供一个查询接口 用户服务(user)提供一个方法供订单服务获取用户信息 服务设计分析 根据情景提要我们可以得知,订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯,相信这里我已经对\"rpc服务存在有什么作用?\"已经作了一个比较好的场景描述。 当然,一个服务开发前远不止这点设计分析,我们这里就不详细描述了。从上文得知,我们需要一个 user rpc order api 两个服务来初步实现这个小demo。 创建mall工程 如果你跑了单体的示例,里面也叫 go-zero-demo,你可能需要换一个父目录。 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 创建user rpc服务 创建user rpc服务 $ mkdir -p mall/user/rpc 添加user.proto文件,增加getUser方法 $ vim mall/user/rpc/user.proto 增加如下代码: syntax = \"proto3\"; package user; // protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成 option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // 用户id string id = 1; // 用户名称 string name = 2; // 用户性别 string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } 生成代码 如未安装 protoc,protoc-gen-go,protoc-gen-grpc-go 你可以通过如下指令一键安装: $ goctl env check -i -f 注意: 1、每一个 *.proto文件只允许有一个service error: only one service expected $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. 填充业务逻辑 $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } 创建order api服务 创建 order api服务 # 回到 go-zero-demo/mall 目录 $ mkdir -p order/api && cd order/api 添加api文件 $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } 生成order服务 $ goctl api go -api order.api -dir . Done. 添加user rpc配置 $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } 添加yaml配置 $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc 完善服务依赖 $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 添加order演示逻辑 给 getorderlogic 添加业务逻辑 $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } 启动服务并验证 启动etcd$ etcd 下载依赖# 在 go-zero-demo 目录下 $ go mod tidy 启动user rpc # 在 mall/user/rpc 目录 $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动order api # 在 mall/order/api 目录 $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... 访问order api $ curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\":\"1\",\"name\":\"test order\"} 注意:在演示中的提及的api语法,rpc生成,goctl,goctl环境等怎么使用和安装,快速入门中不作详细概述,我们后续都会有详细的文档进行描述,你也可以点击下文的【猜你想看】快速跳转的对应文档查看。 源码 mall源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 rpc目录 rpc配置 rpc调用方说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"framework-design.html":{"url":"framework-design.html","title":"框架设计","keywords":"","body":"框架设计 本节将从 go-zero 的设计理念,go-zero 服务的最佳实践目录来说明 go-zero 框架的设计,本节将包含以下小节: go-zero设计理念 go-zero特点 api语法介绍 api目录结构 rpc目录结构 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"go-zero设计理念","keywords":"","body":"go-zero设计理念 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"go-zero特点","keywords":"","body":"go-zero特性 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-grammar.html":{"url":"api-grammar.html","title":"api语法介绍","keywords":"","body":"api语法介绍 api示例 /** * api语法示例及语法说明 */ // api语法版本 syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"songmeizi\" date: \"2020-01-08\" desc: \"api语法示例及语法说明\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } api语法结构 syntax语法声明 import语法块 info语法块 type语法块 service语法块 隐藏通道 [!TIP] 在以上语法结构中,各个语法块从语法上来说,按照语法块为单位,可以在.api文件中任意位置声明, 但是为了提高阅读效率,我们建议按照以上顺序进行声明,因为在将来可能会通过严格模式来控制语法块的顺序。 syntax语法声明 syntax是新加入的语法结构,该语法的引入可以解决: 快速针对api版本定位存在问题的语法结构 针对版本做语法解析 防止api语法大版本升级导致前后不能向前兼容 **[!WARNING] 被import的api必须要和main api的syntax版本一致。 语法定义 'syntax'={checkVersion(p)}STRING 语法说明 syntax:固定token,标志一个syntax语法结构的开始 checkVersion:自定义go方法,检测STRING是否为一个合法的版本号,目前检测逻辑为,STRING必须是满足(?m)\"v[1-9][0-9]*\"正则。 STRING:一串英文双引号包裹的字符串,如\"v1\" 一个api语法文件只能有0或者1个syntax语法声明,如果没有syntax,则默认为v1版本 正确语法示例 ✅ eg1:不规范写法 syntax=\"v1\" eg2:规范写法(推荐) syntax = \"v2\" 错误语法示例 ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" import语法块 随着业务规模增大,api中定义的结构体和服务越来越多,所有的语法描述均为一个api文件,这是多么糟糕的一个问题, 其会大大增加了阅读难度和维护难度,import语法块可以帮助我们解决这个问题,通过拆分api文件, 不同的api文件按照一定规则声明,可以降低阅读难度和维护难度。 **[!WARNING] 这里import不像golang那样包含package声明,仅仅是一个文件路径的引入,最终解析后会把所有的声明都汇聚到一个spec.Spec中。 不能import多个相同路径,否则会解析错误。 语法定义 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' 语法说明 import:固定token,标志一个import语法的开始 checkImportValue:自定义go方法,检测STRING是否为一个合法的文件路径,目前检测逻辑为,STRING必须是满足(?m)\"(/?[a-zA-Z0-9_#-])+\\.api\"正则。 STRING:一串英文双引号包裹的字符串,如\"foo.api\" 正确语法示例 ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) 错误语法示例 ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info语法块 info语法块是一个包含了多个键值对的语法体,其作用相当于一个api服务的描述,解析器会将其映射到spec.Spec中, 以备用于翻译成其他语言(golang、java等) 时需要携带的meta元素。如果仅仅是对当前api的一个说明,而不考虑其翻译 时传递到其他语言,则使用简单的多行注释或者java风格的文档注释即可,关于注释说明请参考下文的 隐藏通道。 **[!WARNING] 不能使用重复的key,每个api文件只能有0或者1个info语法块 语法定义 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' 语法说明 info:固定token,标志一个info语法块的开始 checkKeyValue:自定义go方法,检测VALUE是否为一个合法值。 VALUE:key对应的值,可以为单行的除'\\r','\\n','/'后的任意字符,多行请以\"\"包裹,不过强烈建议所有都以\"\"包裹 正确语法示例 ✅ eg1:不规范写法 info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:规范写法(推荐) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) 错误语法示例 ❌ eg1:没有key-value内容 info() eg2:不包含冒号 info( foo value ) eg3:key-value没有换行 info(foo:\"value\") eg4:没有key info( : \"value\" ) eg5:非法的key info( 12: \"value\" ) eg6:移除旧版本多行语法 info( foo: > some text type语法块 在api服务中,我们需要用到一个结构体(类)来作为请求体,响应体的载体,因此我们需要声明一些结构体来完成这件事情, type语法块由golang的type演变而来,当然也保留着一些golang type的特性,沿用golang特性有: 保留了golang内置数据类型bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, 兼容golang struct风格声明 保留golang关键字 **[!WARNING]️ 不支持alias 不支持time.Time数据类型,用int64表示,因为api支持客户端代码生成,并非所有客户端语言都有time.Time对应的类型 结构体名称、字段名称、不能为golang关键字 语法定义 由于其和golang相似,因此不做详细说明,具体语法定义请在 ApiParser.g4 中查看typeSpec定义。 语法说明 参考golang写法 正确语法示例 ✅ eg1:不规范写法 type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // 非导出型字段 bar int `form:\"bar\"` } type( // 非导出型结构体 fooBar struct{ FooBar int `json:\"fooBar\"` } ) eg2:规范写法(推荐) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int `json:\"fooBar\"` } ) 错误语法示例 ❌ eg type Gender int // 不支持 // 非struct token type Foo structure{ CreateTime time.Time // 不支持time.Time,且没有声明 tag } // golang关键字 var type var{} type Foo{ // golang关键字 interface Foo interface // 没有声明 tag } type Foo{ foo int // map key必须要golang内置数据类型,且没有声明 tag m map[Bar]string } [!NOTE] ① tag定义和golang中json tag语法一样,除了json tag外,go-zero还提供了另外一些tag来实现对字段的描述, 详情见下表。 tag表 绑定参数时,以下四个tag只能选择其中一个 tag key 描述 提供方有效范围 示例 json json序列化tag golang request、response json:\"fooo\" path 路由path,如/foo/:id go-zero request path:\"id\" form 标志请求体是一个form(POST方法时)或者一个query(GET方法时/search?name=keyword) go-zero request form:\"name\" header HTTP header,如 Name: value go-zero request header:\"name\" tag修饰符 常见参数校验描述 tag key 描述 提供方 有效范围 示例 optional 定义当前字段为可选参数 go-zero request json:\"name,optional\" options 定义当前字段的枚举值,多个以竖线|隔开 go-zero request json:\"gender,options=male\" default 定义当前字段默认值 go-zero request json:\"gender,default=male\" range 定义当前字段数值范围 go-zero request json:\"age,range=[0:120]\" [!TIP] tag修饰符需要在tag value后以英文逗号,隔开 service语法块 service语法块用于定义api服务,包含服务名称,服务metadata,中间件声明,路由,handler等。 **[!WARNING]️ main api和被import的api服务名称必须一致,不能出现服务名称歧义。 handler名称不能重复 路由(请求方法+请求path)名称不能重复 请求体必须声明为普通(非指针)struct,响应体做了一些向前兼容处理,详请见下文说明 语法定义 serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; 语法说明 serviceSpec:包含了一个可选语法块atServer和serviceApi语法块,其遵循序列模式(编写service必须要按照顺序,否则会解析出错) atServer: 可选语法块,定义key-value结构的server metadata,'@server' 表示这一个server语法块的开始,其可以用于描述serviceApi或者route语法块,其用于描述不同语法块时有一些特殊关键key 需要值得注意,见 atServer关键key描述说明。 serviceApi:包含了1到多个serviceRoute语法块 serviceRoute:按照序列模式包含了atDoc,handler和route atDoc:可选语法块,一个路由的key-value描述,其在解析后会传递到spec.Spec结构体,如果不关心传递到spec.Spec, 推荐用单行注释替代。 handler:是对路由的handler层描述,可以通过atServer指定handler key来指定handler名称, 也可以直接用atHandler语法块来定义handler名称 atHandler:'@handler' 固定token,后接一个遵循正则[_a-zA-Z][a-zA-Z_-]*)的值,用于声明一个handler名称 route:路由,有httpMethod、path、可选request、可选response组成,httpMethod是必须是小写。 body:api请求体语法定义,必须要由()包裹的可选的ID值 replyBody:api响应体语法定义,必须由()包裹的struct、array(向前兼容处理,后续可能会废弃,强烈推荐以struct包裹,不要直接用array作为响应体) kvLit: 同info key-value serviceName: 可以有多个'-'join的ID值 path:api请求路径,必须以'/'或者'/:'开头,切不能以'/'结尾,中间可包含ID或者多个以'-'join的ID字符串 atServer关键key描述说明 修饰service时 key描述示例 jwt声明当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码jwt: Auth group声明当前service或者路由文件分组group: login middleware声明当前service需要开启中间件middleware: AuthMiddleware prefix添加路由分组prefix: api 修饰route时 key描述示例 handler声明一个handler- 正确语法示例 ✅ eg1:不规范写法 @server( jwt: Auth group: foo middleware: AuthMiddleware prefix api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // 非导出型body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// 不推荐数组作为响应体 @handler fooBar post /foo/bar (Foo) returns // 可以省略'returns' } eg2:规范写法(推荐) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } 错误语法示例 ❌ // 不支持空的server语法块 @server( ) // 不支持空的service语法块 service foo-api{ } service foo-api{ @doc kkkk // 简版doc必须用英文双引号引起来 @handler foo post /foo @handler foo // 重复的handler post /bar @handler fooBar post /bar // 重复的路由 // @handler和@doc顺序错误 @handler someHandler @doc \"some doc\" post /some/path // handler缺失 post /some/path/:id @handler reqTest post /foo/req (*Foo) // 不支持除普通结构体外的其他数据类型作为请求体 @handler replyTest post /foo/reply returns (*Foo) // 不支持除普通结构体、数组(向前兼容,后续考虑废弃)外的其他数据类型作为响应体 } 隐藏通道 隐藏通道目前主要为空白符号、换行符号以及注释,这里我们只说注释,因为空白符号和换行符号我们目前拿来也无用。 单行注释 语法定义 '//' ~[\\r\\n]* 语法说明 由语法定义可知道,单行注释必须要以//开头,内容为不能包含换行符 正确语法示例 ✅ // doc // comment 错误语法示例 ❌ // break line comments java风格文档注释 语法定义 '/*' .*? '*/' 语法说明 由语法定义可知道,单行注释必须要以/*开头,*/结尾的任意字符。 正确语法示例 ✅ /** * java-style doc */ 错误语法示例 ❌ /* * java-style doc */ */ Doc&Comment 如果想获取某一个元素的doc或者comment开发人员需要怎么定义? Doc 我们规定上一个语法块(非隐藏通道内容)的行数line+1到当前语法块第一个元素前的所有注释(单行,或者多行)均为doc, 且保留了//、/*、*/原始标记。 Comment 我们规定当前语法块最后一个元素所在行开始的一个注释块(当行,或者多行)为comment 且保留了//、/*、*/原始标记。 语法块Doc和Comment的支持情况 语法块parent语法块DocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ 以下为对应语法块解析后细带doc和comment的写法 // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * 开启jwt鉴权 */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * route doc * post请求 * path为 /foo * 请求体:Foo * 响应体:Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-dir.html":{"url":"api-dir.html","title":"api目录结构","keywords":"","body":"api目录介绍 . ├── etc │ └── greet-api.yaml // 配置文件 ├── go.mod // mod文件 ├── greet.api // api描述文件 ├── greet.go // main函数入口 └── internal ├── config │ └── config.go // 配置声明type ├── handler // 路由及handler转发 │ ├── greethandler.go │ └── routes.go ├── logic // 业务逻辑 │ └── greetlogic.go ├── middleware // 中间件文件 │ └── greetmiddleware.go ├── svc // logic所依赖的资源池 │ └── servicecontext.go └── types // request、response的struct,根据api自动生成,不建议编辑 └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"rpc目录结构","keywords":"","body":"rpc服务目录 proto 文件 greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations! your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. 生成的目录结构 . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] pb.go & _grpc.pb.go 文件所在目录并非固定,该目录有 go_opt & go-grpc_opt 与 proto文件中的 go_package 值共同决定,想要了解grpc代码生成目录逻辑请阅读 Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"project-dev.html":{"url":"project-dev.html","title":"项目开发","keywords":"","body":"项目开发 在前面的章节我们已经从一些概念、背景、快速入门等维度介绍了一下go-zero,看到这里,相信你对go-zero已经有了一些了解, 从这里开始,我们将会从环境准备到服务部署整个流程开始进行讲解,为了保证大家能够彻底弄懂go-zero的开发流程,那就准备你的耐心来接着往下走吧。 在章节中,将包含以下小节: 准备工作 golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 开发规范 命名规范 路由规范 编码规范 开发流程 配置介绍 api配置 rpc配置 业务开发 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 CI/CD 服务部署 日志收集 链路追踪 服务监控 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare.html":{"url":"prepare.html","title":"准备工作","keywords":"","body":"准备工作 在正式进入实际开发之前,我们需要做一些准备工作,比如:Go环境的安装,grpc代码生成使用的工具安装, 必备工具Goctl的安装,Golang环境配置等,本节将包含以下小节: golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"golang-install.html":{"url":"golang-install.html","title":"golang安装","keywords":"","body":"Golang环境安装 前言 开发golang程序,必然少不了对其环境的安装,我们这里选择以1.15.1为例。 官方文档 https://golang.google.cn/doc/install mac OS安装Go 下载并安装Go for Mac 验证安装结果 $ go version go version go1.15.1 darwin/amd64 linux 安装Go 下载Go for Linux 解压压缩包至/usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz 添加/usr/local/go/bin到环境变量 $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile 验证安装结果 $ go version go version go1.15.1 linux/amd64 Windows安装Go 下载并安装Go for Windows 验证安装结果 $ go version go version go1.15.1 windows/amd64 其他 更多操作系统安装见https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gomod-config.html":{"url":"gomod-config.html","title":"go module配置","keywords":"","body":"Go Module设置 Go Module介绍 Modules are how Go manages dependencies.[1] 即Go Module是Golang管理依赖性的方式,像Java中的Maven,Android中的Gradle类似。 MODULE配置 查看GO111MODULE开启情况 $ go env GO111MODULE on 开启GO111MODULE,如果已开启(即执行go env GO111MODULE结果为on)请跳过。 $ go env -w GO111MODULE=\"on\" 设置GOPROXY $ go env -w GOPROXY=https://goproxy.cn 设置GOMODCACHE 查看GOMODCACHE $ go env GOMODCACHE 如果目录不为空或者/dev/null,请跳过。 go env -w GOMODCACHE=$GOPATH/pkg/mod 参考文档 [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-install.html":{"url":"goctl-install.html","title":"goctl安装","keywords":"","body":"Goctl安装 前言 Goctl在go-zero项目开发着有着很大的作用,其可以有效的帮助开发者大大提高开发效率,减少代码的出错率,缩短业务开发的工作量,更多的Goctl的介绍请阅读Goctl介绍, 在这里我们强烈推荐大家安装,因为后续演示例子中我们大部分都会以goctl进行演示。 安装(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 环境变量检测 go get下载编译后的二进制文件位于$GOPATH/bin目录下,要确保$GOPATH/bin已经添加到环境变量。 $ sudo vim /etc/paths 在最后一行添加如下内容 $GOPATH/bin [!TIP] $GOPATH为你本机上的文件地址 安装结果验证 $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] windows用户添加环境变量请自行google Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go安装","keywords":"","body":"protoc & protoc-gen-go安装 前言 protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。 mac OS方式一:goctl一键安装 $ goctl env check -i -f --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! 方式二: 源文件安装 protoc安装 进入protobuf release 页面,选择适合自己操作系统的压缩包文件 解压protoc-x.x.x-osx-x86_64.zip并进入protoc-x.x.x-osx-x86_64 $ cd protoc-x.x.x-osx-x86_64/bin 将启动的protoc二进制文件移动到被添加到环境变量的任意path下,如$GOPATH/bin,这里不建议直接将其和系统的以下path放在一起。 $ mv protoc $GOPATH/bin [!TIP] $GOPATH为你本机的实际文件夹地址 验证安装结果 $ protoc --version libprotoc x.x.x protoc-gen-go/protoc-gen-go-grpc 安装 下载安装protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest [!WARNING] protoc-gen-go安装失败请阅读常见错误处理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare-other.html":{"url":"prepare-other.html","title":"其他","keywords":"","body":"其他 在之前我们已经对Go环境、Go Module配置、Goctl、protoc & protoc-gen-go安装准备就绪,这些是开发人员在开发阶段必须要准备的环境,而接下来的环境你可以选择性的安装, 因为这些环境一般存在于服务器(安装工作运维会替你完成),但是为了后续演示流程能够完整走下去,我建议大家在本地也安装一下,因为我们的演示环境大部分会以本地为主。 以下仅给出了需要的准备工作,不以文档篇幅作详细介绍了。 其他环境 etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-specification.html":{"url":"dev-specification.html","title":"开发规范","keywords":"","body":"开发规范 在实际业务开发中,除了要提高业务开发效率,缩短业务开发周期,保证线上业务高性能,高可用的指标外,好的编程习惯也是一个开发人员基本素养之一,在本章节, 我们将介绍一下go-zero中的编码规范,本章节为可选章节,内容仅供交流与参考,本章节将从以下小节进行说明: 命名规范 路由规范 编码规范 开发三原则 Clarity(清晰) 作者引用了Hal Abelson and Gerald Sussman的一句话: Programs must be written for people to read, and only incidentally for machines to execute 程序是什么,程序必须是为了开发人员阅读而编写的,只是偶尔给机器去执行,99%的时间程序代码面向的是开发人员,而只有1%的时间可能是机器在执行,这里比例不是重点,从中我们可以看出,清晰的代码是多么的重要,因为所有程序,不仅是Go语言,都是由开发人员编写,供其他人阅读和维护。 Simplicity(简单) Simplicity is prerequisite for reliability Edsger W. Dijkstra认为:可靠的前提条件就是简单,我们在实际开发中都遇到过,这段代码在写什么,想要完成什么事情,开发人员不理解这段代码,因此也不知道如何去维护,这就带来了复杂性,程序越是复杂就越难维护,越难维护就会是程序变得越来越复杂,因此,遇到程序变复杂时首先应该想到的是——重构,重构会重新设计程序,让程序变得简单。 Productivity(生产力) 在go-zero团队中,一直在强调这个话题,开发人员成产力的多少,并不是你写了多少行代码,完成了多少个模块开发,而是我们需要利用各种有效的途径来利用有限的时间完成开发效率最大化,而Goctl的诞生正是为了提高生产力, 因此这个开发原则我是非常认同的。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"naming-spec.html":{"url":"naming-spec.html","title":"命名规范","keywords":"","body":"命名规范 在任何语言开发中,都有其语言领域的一些命名规范,好的命名可以: 降低代码阅读成本 降低维护难度 降低代码复杂度 规范建议 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。 命名准则 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好。 变量命名应尽量描述其内容,而不是类型 常量命名应尽量描述其值,而不是如何使用这个值 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值 method、interface、type、package推荐使用单词命名 package名称也是命名的一部分,请尽量将其利用起来 使用一致的命名风格 文件命名规范 全部小写 除unit test外避免下划线(_) 文件名称不宜过长 变量命名规范参考 首字母小写 驼峰命名 见名知义,避免拼音替代英文 不建议包含下划线(_) 不建议包含数字 适用范围 局部变量 函数出参、入参 函数、常量命名规范 驼峰式命名 可exported的必须首字母大写 不可exported的必须首字母小写 避免全部大写与下划线(_)组合 [!TIP] 如果是go-zero代码贡献,则必须严格遵循此命名规范 参考文档 Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"路由规范","keywords":"","body":"路由规范 推荐脊柱式命名 小写单词、横杠(-)组合 见名知义 /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"coding-spec.html":{"url":"coding-spec.html","title":"编码规范","keywords":"","body":"编码规范 import 单行import不建议用圆括号包裹 按照官方包,NEW LINE,当前工程包,NEW LINE,第三方依赖包顺序引入 import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) 函数返回 对象避免非指针返回 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则 错误处理 有error必须处理,如果不能处理就必须抛出。 避免下划线(_)接收error 函数体编码 建议一个block结束空一行,如if、for等 func main (){ if x==1{ // do something } fmt.println(\"xxx\") } return前空一行 func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-flow.html":{"url":"dev-flow.html","title":"开发流程","keywords":"","body":"开发流程 这里的开发流程和我们实际业务开发流程不是一个概念,这里的定义局限于go-zero的使用,即代码层面的开发细节。 开发流程 goctl环境准备[1] 数据库设计 业务开发 新建工程 创建服务目录 创建服务类型(api/rpc/rmq/job/script) 编写api、proto文件 代码生成 生成数据库访问层代码model 配置config,yaml变更 资源依赖填充(ServiceContext) 添加中间件 业务代码填充 错误处理 [!TIP] [1] goctl环境 开发工具 Visual Studio Code Goland(推荐) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"config-introduction.html":{"url":"config-introduction.html","title":"配置介绍","keywords":"","body":"配置介绍 在正式使用go-zero之前,让我们先来了解一下go-zero中不同服务类型的配置定义,看看配置中每个字段分别有什么作用,本节将包含以下小节: api配置 rpc配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-config.html":{"url":"api-config.html","title":"api配置","keywords":"","body":"api配置 api配置控制着api服务中的各种功能,包含但不限于服务监听地址,端口,环境配置,日志配置等,下面我们从一个简单的配置来看一下api中常用配置分别有什么作用。 配置说明 通过yaml配置我们会发现,有很多参数我们并没有与config对齐,这是因为config定义中,有很多都是带optional或者default 标签的,对于optional可选项,你可以根据自己需求判断是否需要设置,对于default标签,如果你觉得默认值就已经够了,可以不用设置, 一般default中的值基本不用修改,可以认为是最佳实践值。 Config type Config struct{ rest.RestConf // rest api配置 Auth struct { // jwt鉴权配置 AccessSecret string // jwt密钥 AccessExpire int64 // 有效期,单位:秒 } Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库 DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可 } CacheRedis cache.CacheConf // redis缓存 UserRpc zrpc.RpcClientConf // rpc client配置 } rest.RestConf api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制,对其展开我们可以看到: service.ServiceConf // service配置 Host string `json:\",default=0.0.0.0\"` // http监听ip,默认0.0.0.0 Port int // http监听端口,必填 CertFile string `json:\",optional\"` // https证书文件,可选 KeyFile string `json:\",optional\"` // https私钥文件,可选 Verbose bool `json:\",optional\"` // 是否打印详细http请求日志 MaxConns int `json:\",default=10000\"` // http同时可接受最大请求数(限流数),默认10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http可接受请求的最大ContentLength,默认1048576,被设置值必须在0到8388608之间 // milliseconds Timeout int64 `json:\",default=3000\"` // 超时时长控制,单位:毫秒,默认3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // cpu降载阈值,默认900,可允许设置范围0到1000 Signature SignatureConf `json:\",optional\"` // 签名配置 service.ServiceConf type ServiceConf struct { Name string // 服务名称 Log logx.LogConf // 日志配置 Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // 服务环境,dev-开发环境,test-测试环境,pre-预发环境,pro-正式环境 MetricsUrl string `json:\",optional\"` // 指标上报接口地址,该地址需要支持post json即可 Prometheus prometheus.Config `json:\",optional\"` // prometheus配置 } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // 服务名称 Mode string `json:\",default=console,options=console|file|volume\"` // 日志模式,console-输出到console,file-输出到当前服务器(容器)文件,,volume-输出docker挂载文件内 Path string `json:\",default=logs\"` // 日志存储路径 Level string `json:\",default=info,options=info|error|severe\"` // 日志级别 Compress bool `json:\",optional\"` // 是否开启gzip压缩 KeepDays int `json:\",optional\"` // 日志保留天数 StackCooldownMillis int `json:\",default=100\"` // 日志write间隔 } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus 监听host Port int `json:\",default=9101\"` // prometheus 监听端口 Path string `json:\",default=/metrics\"` // 上报地址 } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // 是否Strict模式,如果是则PrivateKeys必填 Expiry time.Duration `json:\",default=1h\"` // 有效期,默认1小时 PrivateKeys []PrivateKeyConf // 签名密钥相关配置 } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // 指纹配置 KeyFile string // 密钥配置 } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // 权重 } redis.RedisConf RedisConf struct { Host string // redis地址 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-config.html":{"url":"rpc-config.html","title":"rpc配置","keywords":"","body":"rpc配置 rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等,下面我们以一个常见的rpc服务配置来进行说明。 配置说明 Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // redis缓存配置,详情见api配置说明,这里不赘述 Mysql struct { // mysql数据库访问配置,详情见api配置说明,这里不赘述 DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // 服务配置,详情见api配置说明,这里不赘述 ListenOn string // rpc监听地址和端口,如:127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd相关配置 Auth bool `json:\",optional\"` // 是否开启Auth,如果是则Redis为必填 Redis redis.RedisKeyConf `json:\",optional\"` // Auth验证 StrictControl bool `json:\",optional\"` // 是否Strict模式,如果是则遇到错误是Auth失败,否则可以认为成功 // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // 超时控制,单位:毫秒 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` cpu降载阈值,默认900,可允许设置范围0到1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host数组 Key string // rpc注册key } redis.RedisKeyConf RedisConf struct { Host string // redis 主机 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // 验证key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-dev.html":{"url":"business-dev.html","title":"业务开发","keywords":"","body":"业务开发 本章节我们用一个简单的示例去演示一下go-zero中的一些基本功能。本节将包含以下小节: 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 演示工程下载 在正式进入后续文档叙述前,可以先留意一下这里的源码,后续我们会基于这份源码进行功能的递进式演示, 而不是完全从0开始,如果你从快速入门章节过来,这份源码结构对你来说不是问题。 点击这里下载演示工程基础源码 演示工程说明 场景 程序员小明需要借阅一本《西游记》,在没有线上图书管理系统的时候,他每天都要去图书馆前台咨询图书馆管理员, 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,明天再来看看吧。 过了一天,小明又来到图书馆,问: 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,你过两天再来看看吧。 就这样经过多次反复,小明也是徒劳无功,浪费大量时间在来回的路上,于是终于忍受不了落后的图书管理系统, 他决定自己亲手做一个图书查阅系统。 预期实现目标 用户登录 依靠现有学生系统数据进行登录 图书检索 根据图书关键字搜索图书,查询图书剩余数量。 系统分析 服务拆分 user api 提供用户登录协议 rpc 供search服务访问用户数据 search api 提供图书查询协议 [!TIP] 这个微小的图书借阅查询系统虽然小,从实际来讲不太符合业务场景,但是仅上面两个功能,已经满足我们对go-zero api/rpc的场景演示了, 后续为了满足更丰富的go-zero功能演示,会在文档中进行业务插入即相关功能描述。这里仅用一个场景进行引入。 注意:user中的sql语句请自行创建到db中去,更多准备工作见准备工作 添加一些预设的用户数据到数据库,便于后面使用,为了篇幅,演示工程不对插入数据这种操作做详细演示。 参考预设数据 INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-design.html":{"url":"service-design.html","title":"目录拆分","keywords":"","body":"目录拆分 目录拆分是指配合go-zero的最佳实践的目录拆分,这和微服务拆分有着关联,在团队内部最佳实践中, 我们按照业务横向拆分,将一个系统拆分成多个子系统,每个子系统应拥有独立的持久化存储,缓存系统。 如一个商城系统需要有用户系统(user),商品管理系统(product),订单系统(order),购物车系统(cart),结算中心系统(pay),售后系统(afterSale)等组成。 系统结构分析 在上文提到的商城系统中,每个系统在对外(http)提供服务的同时,也会提供数据给其他子系统进行数据访问的接口(rpc),因此每个子系统可以拆分成一个服务,而且对外提供了两种访问该系统的方式api和rpc,因此, 以上系统按照目录结构来拆分有如下结构: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc调用链建议 在设计系统时,尽量做到服务之间调用链是单向的,而非循环调用,例如:order服务调用了user服务,而user服务反过来也会调用order的服务, 当其中一个服务启动故障,就会相互影响,进入死循环,你order认为是user服务故障导致的,而user认为是order服务导致的,如果有大量服务存在相互调用链, 则需要考虑服务拆分是否合理。 常见服务类型的目录结构 在上述服务中,仅列举了api/rpc服务,除此之外,一个服务下还可能有其他更多服务类型,如rmq(消息处理系统),cron(定时任务系统),script(脚本)等, 因此一个服务下可能包含以下目录结构: user ├── api // http访问服务,业务需求实现 ├── cronjob // 定时任务,定时数据更新业务 ├── rmq // 消息处理系统:mq和dq,处理一些高并发和延时消息业务 ├── rpc // rpc服务,给其他子系统提供基础数据访问 └── script // 脚本,处理一些临时运营需求,临时数据修复 完整工程目录结构示例 mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script 猜你想看 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"model-gen.html":{"url":"model-gen.html","title":"model生成","keywords":"","body":"model生成 首先,下载好演示工程 后,我们以user的model来进行代码生成演示。 前言 model是服务访问持久化数据层的桥梁,业务的持久化数据常存在于mysql,mongo等数据库中,我们都知道,对于一个数据库的操作莫过于CURD, 而这些工作也会占用一部分时间来进行开发,我曾经在编写一个业务时写了40个model文件,根据不同业务需求的复杂性,平均每个model文件差不多需要 10分钟,对于40个文件来说,400分钟的工作时间,差不多一天的工作量,而goctl工具可以在10秒钟来完成这400分钟的工作。 准备工作 进入演示工程book,找到user/model下的user.sql文件,将其在你自己的数据库中执行建表。 代码生成(带缓存) 方式一(ddl) 进入service/user/model目录,执行命令 $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. 方式二(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource为数据库连接地址 方式三(intellij 插件) 在Goland中,右键user.sql,依次进入并点击New->Go Zero->Model Code即可生成,或者打开user.sql文件, 进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Mode Code即可 [!TIP] intellij插件生成需要安装goctl插件,详情见intellij插件 验证生成的model文件 查看tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go 更多 对于持久化数据,如果需要更灵活的数据库能力,包括事务能力,可以参考 Mysql 如果需要分布式事务的能力,可以参考 分布式事务支持 猜你想看 model命令及其原理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-coding.html":{"url":"api-coding.html","title":"api文件编写","keywords":"","body":"api文件编写 编写user.api文件 $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } 生成api服务 方式一 $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. 方式二 在 user.api 文件右键,依次点击进入 New->Go Zero->Api Code ,进入目标目录选择,即api源码的目标存放目录,默认为user.api所在目录,选择好目录后点击OK即可。 方式三 打开user.api,进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Api Code,同样进入目录选择弹窗,选择好目录后点击OK即可。 猜你想看 api语法 goctl api命令 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-coding.html":{"url":"business-coding.html","title":"业务编码","keywords":"","body":"业务编码 前面一节,我们已经根据初步需求编写了user.api来描述user服务对外提供哪些服务访问,在本节我们接着前面的步伐, 通过业务编码来讲述go-zero怎么在实际业务中使用。 添加Mysql配置 $ vim service/user/api/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/core/stores/cache\" ) type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } 完善yaml配置 $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 更多配置信息,请参考api配置介绍 完善服务依赖 $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } 填充登录逻辑 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"用户密码不正确\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] 上述代码中 [start]-[end]的代码实现见jwt鉴权章节 猜你想看 api语法 goctl api命令 api目录结构介绍 jwt鉴权 api配置介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"jwt.html":{"url":"jwt.html","title":"jwt鉴权","keywords":"","body":"jwt鉴权 概述 JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。 什么时候应该使用JWT 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。 信息交换:JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。 为什么要使用JSON Web令牌 由于JSON不如XML冗长,因此在编码时JSON的大小也较小,从而使JWT比SAML更为紧凑。这使得JWT是在HTML和HTTP环境中传递的不错的选择。 在安全方面,只能使用HMAC算法由共享机密对SWT进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公用/专用密钥对进行签名。与签署JSON的简单性相比,使用XML Digital Signature签署XML而不引入模糊的安全漏洞是非常困难的。 JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象的映射。与SAML断言相比,这使使用JWT更加容易。 关于用法,JWT是在Internet规模上使用的。这突显了在多个平台(尤其是移动平台)上对JSON Web令牌进行客户端处理的简便性。 [!TIP] 以上内容全部来自jwt官网介绍 go-zero中怎么使用jwt jwt鉴权一般在api层使用,我们这次演示工程中分别在user api登录时生成jwt token,在search api查询图书时验证用户jwt token两步来实现。 user api生成jwt token 接着业务编码章节的内容,我们完善上一节遗留的getJwtToken方法,即生成jwt token逻辑 添加配置定义和yaml配置项 $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:生成jwt token的密钥,最简单的方式可以使用一个uuid值。 $AccessExpire:jwt token有效期,单位:秒 更多配置信息,请参考api配置介绍 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search api使用jwt token鉴权 编写search.api文件 $ vim service/search/api/search.api type ( SearchReq { // 图书名称 Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth:开启jwt鉴权 如果路由需要jwt鉴权,则需要在service上方声明此语法标志,如上文中的/search/do 不需要jwt鉴权的路由就无需声明,如上文中/search/ping 更多语法请阅读api语法介绍 生成代码 前面已经描述过有三种方式去生成代码,这里就不赘述了。 添加yaml配置项 $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 这里修改一下端口,避免和user api端口8888冲突 验证 jwt token 启动user api服务,登录 $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'Content-Type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' 如果是在Windows的CMD里运行,命令格式如下: curl -i -X POST http://127.0.0.1:8888/user/login -H \"Content-Type: application/json\" -d \"{ \\\"username\\\":\\\"666\\\", \\\"password\\\":\\\"123456\\\" }\" 访问结果: HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"小明\",\"gender\":\"男\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} 启动search api服务,调用/search/do验证jwt鉴权是否通过 $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... 我们先不传jwt token,看看结果 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 很明显,jwt鉴权失败了,返回401的statusCode,接下来我们带一下jwt token(即用户登录返回的accessToken) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] 服务启动错误,请查看常见错误处理 至此,jwt从生成到使用就演示完成了,jwt token的鉴权是go-zero内部已经封装了,你只需在api文件中定义服务时简单的声明一下即可。 获取jwt token中携带的信息 go-zero从jwt token解析后会将用户生成token时传入的kv原封不动的放在http.Request的Context中,因此我们可以通过Context就可以拿到你想要的值 $ vim /service/search/api/internal/logic/searchlogic.go 添加一个log来输出从jwt解析出来的userId。 func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } 运行结果 {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} 猜你想看 jwt介绍 api配置介绍 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"middleware.html":{"url":"middleware.html","title":"中间件使用","keywords":"","body":"中间件使用 在上一节,我们演示了怎么使用jwt鉴权,相信你已经掌握了对jwt的基本使用,本节我们来看一下api服务中间件怎么使用。 中间件分类 在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,其和jwt类似,没有放在jwt:xxx下的路由不会使用中间件功能, 而全局中间件的服务范围则是整个服务。 中间件使用 这里以search服务为例来演示中间件的使用 路由中间件 重新编写search.api文件,添加middleware声明 $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // 路由中间件声明 ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } 重新生成api代码 $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. 生成完后会在internal目录下多一个middleware的目录,这里即中间件文件,后续中间件的实现逻辑也在这里编写。 完善资源依赖ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } 编写中间件逻辑 这里仅添加一行日志,内容example middle,如果服务运行输出example middle则代表中间件使用起来了。 $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } 启动服务验证 {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} 全局中间件 通过rest.Server提供的Use方法即可 func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // 全局中间件 server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} 在中间件里调用其它服务 通过闭包的方式把其它服务传递给中间件,示例如下: // 模拟的其它服务 type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // 常规中间件 func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // 调用其它服务的中间件 func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } 完整代码参考:https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-call.html":{"url":"rpc-call.html","title":"rpc服务编写与调用","keywords":"","body":"rpc编写与调用 在一个大的系统中,多个子系统(服务)间必然存在数据传递,有数据传递就需要通信方式,你可以选择最简单的http进行通信,也可以选择rpc服务进行通信, 在go-zero,我们使用zrpc来进行服务间的通信,zrpc是基于grpc。 场景 在前面我们完善了对用户进行登录,用户查询图书等接口协议,但是用户在查询图书时没有做任何用户校验,如果当前用户是一个不存在的用户则我们不允许其查阅图书信息, 从上文信息我们可以得知,需要user服务提供一个方法来获取用户信息供search服务使用,因此我们就需要创建一个user rpc服务,并提供一个getUser方法。 rpc服务编写 编译proto文件 $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } 生成rpc服务代码$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] 如果安装的 protoc-gen-go 版大于1.4.0, proto文件建议加上go_package 添加配置及完善yaml配置项 $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 $etcdHost: etcd连接地址,格式:ip:port,如: 127.0.0.1:2379 更多配置信息,请参考rpc配置介绍 添加资源依赖 $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } 添加rpc逻辑 $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } 使用rpc 接下来我们在search服务中调用user rpc 添加UserRpc配置及yaml配置项 $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 $etcdHost: etcd连接地址 etcd中的Key必须要和user rpc服务配置中Key一致 添加依赖 $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 补充逻辑 $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // 使用user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } 启动并验证服务 启动etcd、redis、mysql 启动user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml 验证服务 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"西游记\",\"count\":100} 猜你想看 rpc配置 rpc服务目录 goctl rpc命令 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error-handle.html":{"url":"error-handle.html","title":"错误处理","keywords":"","body":"错误处理 错误的处理是一个服务必不可缺的环节。在平时的业务开发中,我们可以认为http状态码不为2xx系列的,都可以认为是http请求错误, 并伴随响应的错误信息,但这些错误信息都是以plain text形式返回的。除此之外,我在业务中还会定义一些业务性错误,常用做法都是通过 code、msg 两个字段来进行业务处理结果描述,并且希望能够以json响应体来进行响应。 业务错误响应格式 业务处理正常 { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } 业务处理异常 { \"code\": 10001, \"msg\": \"参数错误\" } user api之login 在之前,我们在登录逻辑中处理用户名不存在时,直接返回来一个error。我们来登录并传递一个不存在的用户名看看效果。 curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 用户名不存在 接下来我们将其以json格式进行返回 自定义错误 首先在common中添加一个baseerror.go文件,并填入代码 $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } 将登录逻辑中错误用CodeError自定义错误替换 if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"用户密码不正确\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil 开启自定义错误 $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // 自定义错误 httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } 重启服务验证 $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"用户名不存在\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD 在软件工程中,CI/CD或CICD通常指的是持续集成和持续交付或持续部署的组合实践。 ——引自维基百科 CI可以做什么? 现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为“合并日”),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。 持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或“主干”中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。 ——引自《CI/CD是什么?如何理解持续集成、持续交付和持续部署》 从概念上来看,CI/CD包含部署过程,我们这里将部署(CD)单独放在一节服务部署, 本节就以gitlab来做简单的CI(Run Unit Test)演示。 gitlab CI Gitlab CI/CD是Gitlab内置的软件开发工具,提供 持续集成(CI) 持续交付(CD) 持续部署(CD) 准备工作 gitlab安装 git安装 gitlab runner安装 开启gitlab CI 上传代码 在gitlab新建一个仓库go-zero-demo 将本地代码上传到go-zero-demo仓库 在项目根目录下创建.gitlab-ci.yaml文件,通过此文件可以创建一个pipeline,其会在代码仓库中有内容变更时运行,pipeline由一个或多个按照顺序运行, 每个阶段可以包含一个或者多个并行运行的job。 添加CI内容(仅供参考) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] 以上CI为简单的演示,详细的gitlab CI请参考gitlab官方文档进行更丰富的CI集成。 参考文档 CI/CD 维基百科 CI/CD是什么?如何理解持续集成、持续交付和持续部署 Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-deployment.html":{"url":"service-deployment.html","title":"服务部署","keywords":"","body":"服务部署 本节通过jenkins来进行简单的服务部署到k8s演示。 准备工作 k8s集群安装 gitlab环境安装 jenkins环境安装 redis&mysql&nginx&etcd安装 goctl安装 [!TIP] goctl确保k8s每个node节点上都有 以上环境安装请自行google,这里不做篇幅介绍。 服务部署 1、gitlab代码仓库相关准备 1.1、添加SSH Key 进入gitlab,点击用户中心,找到Settings,在左侧找到SSH Keystab 1、在jenkins所在机器上查看公钥 $ cat ~/.ssh/id_rsa.pub 2、如果没有,则需要生成,如果存在,请跳转到第3步 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 完成生成后,重复第一步操作 3、将公钥添加到gitlab中 1.2、上传代码到gitlab仓库 新建工程go-zero-demo并上传代码,这里不做细节描述。 2、jenkins 2.1、添加凭据 查看jenkins所在机器的私钥,与前面gitlab公钥对应 $ cat id_rsa 进入jenkins,依次点击Manage Jenkins-> Manage Credentials 进入全局凭据页面,添加凭据,Username是一个标识,后面添加pipeline你知道这个标识是代表gitlab的凭据就行,Private Key`即上面获取的私钥 2.2、 添加全局变量 进入Manage Jenkins->Configure System,滑动到全局属性条目,添加docker私有仓库相关信息,如图为docker用户名、docker用户密码、docker私有仓库地址 [!TIP] docker_user 修改为你的docker用户名 docker_pass 修改为你的docker用户密码 docker_server 修改为你的docker服务器地址 这里我使用的私有仓库,如果没有云厂商提供的私有仓库使用,可以自行搭建一个私有仓库,这里就不赘述了,大家自行google。 2.3、配置git 进入Manage Jenkins->Global Tool Configureation,找到Git条目,填写jenkins所在机器git可执行文件所在path,如果没有的话,需要在jenkins插件管理中下载Git插件。 2.4、 添加一个Pipeline pipeline用于构建项目,从gitlab拉取代码->生成Dockerfile->部署到k8s均在这个步骤去做,这里是演示环境,为了保证部署流程顺利, 需要将jenkins安装在和k8s集群的其中过一个节点所在机器上,我这里安装在master上的。 获取凭据id 进入凭据页面,找到Username为gitlab的凭据id 进入jenkins首页,点击新建Item,名称为user 查看项目git地址 添加服务类型Choice Parameter,在General中勾选This project is parameterized,点击添加参数选择Choice Parameter,按照图中添加选择的值常量(api、rpc)及接收值的变量(type),后续在Pipeline script中会用到。 配置user,在user配置页面,向下滑动找到Pipeline script,填写脚本内容 pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: '选择需要构建的分支' } stages { stage('服务信息') { steps { sh 'echo 分支:$branch' sh 'echo 构建服务类型:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('获取commit_id') { steps { echo '获取commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl版本检测') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo 镜像名称:${image}' sh 'docker build -t ${image} .' } } stage('上传到镜像仓库') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('部署到k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId}要替换为你的具体凭据值,即【添加凭据】模块中的一串字符串,${gitUrl}需要替换为你代码的git仓库地址,其他的${xxx}形式的变量无需修改,保持原样即可。 port.sh参考内容如下 case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac 其中dockerlogin.sh内容 #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker登录用户名 $docker-pass: docker登录用户密码 $docker-server: docker私有地址 查看pipeline 查看k8s服务 猜你想看 goctl安装 k8s介绍 docker介绍 jenkins安装 jenkins pipeline nginx文档介绍 etcd文档说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"log-collection.html":{"url":"log-collection.html","title":"日志收集","keywords":"","body":"日志收集 为了保证业务稳定运行,预测服务不健康风险,日志的收集可以帮助我们很好的观察当前服务的健康状况, 在传统业务开发中,机器部署还不是很多时,我们一般都是直接登录服务器进行日志查看、调试,但随着业务的增大,服务的不断拆分, 服务的维护成本也会随之变得越来越复杂,在分布式系统中,服务器机子增多,服务分布在不同的服务器上,当遇到问题时, 我们不能使用传统做法,登录到服务器进行日志排查和调试,这个复杂度可想而知。 [!TIP] 如果是一个简单的单体服务系统或者服务过于小不建议直接使用,否则会适得其反。 准备工作 kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash filebeat配置 $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # 开启json解析 json.keys_under_root: true json.add_error_key: true # 日志文件路径 paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # 定义kafka topic field fields: log_topic: log-collection # 输出到kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx为filebeat.yaml所在路径 go-stash配置 新建config.yaml文件 添加配置内容 $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC 启动服务(按顺序启动) 启动kafka 启动elasticsearch 启动kibana 启动go-stash 启动filebeat 启动order-api服务及其依赖服务(go-zero-demo工程中的order-api服务) 访问kibana 进入127.0.0.1:5601 [!TIP] 这里仅演示收集服务中通过logx产生的日志,nginx中日志收集同理。 参考文档 kafka elasticsearch kibana filebeat go-stash filebeat配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"trace.html":{"url":"trace.html","title":"链路追踪","keywords":"","body":"go-zero链路追踪 序言 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http 。而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪。 追踪的方法就是在一个请求开始时生成一个自己的 spanID ,随着整个请求链路传下去。我们则通过这个 spanID 查看整个链路的情况和性能问题。 下面来看看 go-zero 的链路实现。 代码结构 spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 概念 SpanContext 在介绍 span 之前,先引入 context 。SpanContext 保存了分布式追踪的上下文信息,包括 Trace id,Span id 以及其它需要传递到下游的内容。OpenTracing 的实现需要将 SpanContext 通过某种协议 进行传递,以将不同进程中的 Span 关联到同一个 Trace 上。对于 HTTP 请求来说,SpanContext 一般是采用 HTTP header 进行传递的。 下面是 go-zero 默认实现的 spanContext type spanContext struct { traceId string // TraceID 表示tracer的全局唯一ID spanId string // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一 } 同时开发者也可以实现 SpanContext 提供的接口方法,实现自己的上下文信息传递: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // 自定义操作TraceId,SpanId } Span 一个 REST 调用或者数据库操作等,都可以作为一个 span 。 span 是分布式追踪的最小跟踪单位,一个 Trace 由多段 Span 组成。追踪信息包含如下信息: type Span struct { ctx spanContext // 传递的上下文 serviceName string // 服务名 operationName string // 操作 startTime time.Time // 开始时间戳 flag string // 标记开启trace是 server 还是 client children int // 本 span fork出来的 childsnums } 从 span 的定义结构来看:在微服务中, 这就是一个完整的子调用过程,有调用开始 startTime ,有标记自己唯一属性的上下文结构 spanContext 以及 fork 的子节点数。 实例应用 在 go-zero 中http,rpc中已经作为内置中间件集成。我们以 http ,rpc 中,看看 tracing 是怎么使用的: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } 将 header -> carrier,获取 header 中的traceId等信息 开启一个新的 span,并把「traceId,spanId」封装在context中 从上述的 carrier「也就是header」获取traceId,spanId 看header中是否设置 如果没有设置,则随机生成返回 从 request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回 从上述的 context,拷贝一份到当前的 request 这样就实现了 span 的信息随着 request 传递到下游服务。 RPC 在 rpc 中存在 client, server ,所以从 tracing 上也有 clientTracing, serverTracing 。 serveTracing 的逻辑基本与 http 的一致,来看看 clientTracing 是怎么使用的? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** 将 pair 中的data以map的形式加入 ctx ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } 获取上游带下来的 span 上下文信息 从获取的 span 中创建新的 ctx,span「继承父span的traceId」 将生成 span 的data加入ctx,传递到下一个中间件,流至下游 总结 go-zero 通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。开发者可以通过 ELK 工具追踪 traceID ,看到整个调用链。 同时 go-zero 并没有提供整套 trace 链路方案,开发者可以封装 go-zero 已有的 span 结构,做自己的上报系统,接入 jaeger, zipkin 等链路追踪工具。 参考 go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-monitor.html":{"url":"service-monitor.html","title":"服务监控","keywords":"","body":"服务监控 在微服务治理中,服务监控也是非常重要的一个环节,监控一个服务是否正常工作,需要从多维度进行,如: mysql指标 mongo指标 redis指标 请求日志 服务指标统计 服务健康检测 ... 监控的工作非常大,本节仅以其中的服务指标监控作为例子进行说明。 基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 $ prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl goctl是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有: api服务生成 rpc服务生成 model代码生成 模板管理 本节将包含以下内容: 自动补全设置 命令大全 api命令 rpc命令 model命令 plugin命令 其他命令 goctl 读音 很多人会把 goctl 读作 go-C-T-L,这种是错误的念法,应参照 go control 读做 ɡō kənˈtrōl。 查看版本信息 $ goctl -v 如果安装了goctl则会输出以下格式的文本信息: goctl version ${version} ${os}/${arch} 例如输出: goctl version 1.1.5 darwin/amd64 版本号说明 version:goctl 版本号 os:当前操作系统名称 arch: 当前系统架构名称 安装 goctl 方式一(go get) # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 通过此命令可以将goctl工具安装到 $GOPATH/bin 目录下 方式二 (fork and build) 从 go-zero代码仓库 git@github.com:zeromicro/go-zero.git 拉取一份源码,进入 tools/goctl/目录下编译一下 goctl 文件,然后将其添加到环境变量中。 安装完成后执行goctl -v,如果输出版本信息则代表安装成功,例如: $ goctl -v goctl version 1.1.4 darwin/amd64 常见问题 command not found: goctl 请确保goctl已经安装,或者goctl是否已经正确添加到当前shell的环境变量中。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"自动补全设置","keywords":"","body":"goctl自动补全 goctl 自动补全仅支持 unix-like 操作系统 用法 $ goctl completion -h NAME: goctl completion - generation completion script, it only works for unix-like OS USAGE: goctl completion [command options] [arguments...] OPTIONS: --name value, -n value the filename of auto complete script, default is [goctl_autocomplete] 生成自动补全文件 $ goctl completion generation auto completion success! executes the following script to setting shell: echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell 配置 zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc 演示效果 使用 tab 键出现自动补全提示 $ goctl api -- generate api related files bug -- report a bug completion -- generation completion script, it only works for unix-like OS docker -- generate Dockerfile help h -- Shows a list of commands or help for one command kube -- generate kubernetes files migrate -- migrate from tal-tech to zeromicro model -- generate model code rpc -- generate rpc code template -- template operation upgrade -- upgrade goctl to latest version Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"命令大全","keywords":"","body":"goctl命令大全 goctl bug (报告一个错误) upgrade (将goctl升级到最新版本) env (检查或编辑goctl环境) --write, -w: 编辑goctl环境 check (检测goctl环境和依赖性工具) --force, -f: 默许安装不存在的依赖项 --install, -i: 如果没有找到,就安装依赖工具 migrate (从tal-tech迁移到zeromicro) --verbose, -v: verbose可以实现额外的日志记录 --version: 要迁移的github.com/zeromicro/go-zero的目标版本。 api (生成api相关文件) --branch:远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 -o:输出api文件 new (快速创建api服务) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (格式化api文件) --declare:用于跳过检查已经声明的api类型 --dir: 格式目标目录 --iu: 忽略更新 --stdin:使用stdin输入api文件内容,按 \"ctrl + d \"发送EOF。 validate (验证api文件) --api: 验证目标api文件 doc (生成文档文件) --dir: 目标目录 --o: 输出markdown目录 go (提供的api生成go文件) --api: api文件 --branch: 远程 repo 的分支,它与 --remote 一起工作。 --dir: 目标目录 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (为api文件中提供的api生成java文件) --api: api文件 --dir: 目标目录 ts (为api文件中提供的api生成ts文件) --api: api文件 --caller: 网络api调用者 --dir: 目标目录 --unwrap: 解除webapi调用器的包装,以便导入 --webapi: web api文件的路径 dart (为api文件中提供的api生成dart文件) --api: api文件 --dir: 目标目录 --hostname: 服务器的主机名 --legacy: 用于flutter v1的传统生成器 kt (为提供的api文件生成kotlin代码) --api: api文件 --dir: 目标目录 --pkg: 定义kotlin文件的包名 plugin (自定义文件生成器) --api: api文件 --dir: 目标目录 --plugin, -p: 插件文件 --style: 文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (生成Docker文件) --branch:远程版本库的分支,它与--remote一起工作。 --go:包含主函数的文件 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --port:要公开的端口,默认为无(默认:0)。 --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --scratch:使用scratch作为基础docker镜像 --tz:容器的时区(默认:亚洲/上海) --version:goctl builder golang镜像的版本。 kube (生成kubernetes文件) deploy (生成部署yaml文件) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --image:部署的docker镜像 --limitCpu:部署的cpu上限(默认为1000)。 --limitMem: 部署的内存上限(默认为1024)。 --maxReplicas: 部署的最大复制数(默认为10)。 --minReplicas: 部署的最小复制量(默认为3)。 --name:部署的名称 --namespace:部署的命名空间 --nodePort: 要公开的部署的nodePort(默认为0)。 --port: 要在pod上监听的部署的端口(默认值:0) --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote有更高的优先级。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --replicas:要部署的副本数量(默认:3个)。 --requestCpu:要部署的请求cpu(默认为500)。 --requestMem: 要部署的请求内存(默认为512)。 --revisions: 限制修订历史的数量(默认为5)。 --secret: 从注册表中提取镜像的秘密。 --serviceAccount:部署的ServiceAccount。 -o: 输出的yaml文件 rpc (生成rpc代码) new (生成rpc演示服务) --branch: 远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 --idea:命令执行环境是否来自idea插件。[可选] --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (生成proto模板) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl主路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --out, -o: proto的目标路径 --remote:模板的远程git repo,--home和--remote不能同时设置,如果有的话,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 protoc (生成grpc代码) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl主路径 --remote: 模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out:zrpc的输出目录 model (生成model代码) mysql (生成mysql模型) ddl (从ddl生成mysql模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --database, --db:数据库的名称 [可选] - --dir, -d: 目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --src, -s:ddl的路径或路径globbing模式 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c: 使用缓存生成代码 [可选] - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t:数据库中的表或表球化模式 --url:数据库的数据源,如 \"root:password@tcp(127.0.0.1:3306)/database\" pg (生成postgresql模型) datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --schema, -s:表的模式,默认为[public] --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: 数据库中的表或表球化模式 --url:数据库的数据源,如 \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (生成mongo模型) --branch:远程repo的分支,它与--remote一起工作。 --cache, -c: 使用缓存生成代码 [可选] --dir, -d:目标目录 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t:指定的模型类型名称 template (模板操作) init (初始化所有模板(强制更新)) --home: 模板的goctl主路径 clean (清理所有缓存的模板) --home: 模板的goctl主路径 update (将目标类别的模板更新为最新的) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] --home: 模板的goctl主页路径 revert (将目标模板恢复到最新版本) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] 。 --home:模板的goctl主路径 --name, -n: 模板的目标文件名 completion (生成自动补全脚本,它只适用于类unix操作系统) --name, -n:自动完成脚本的文件名,默认为[goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-api.html":{"url":"goctl-api.html","title":"api命令","keywords":"","body":"api命令 goctl api是goctl中的核心模块之一,其可以通过.api文件一键快速生成一个api服务,如果仅仅是启动一个go-zero的api演示项目, 你甚至都不用编码,就可以完成一个api服务开发及正常运行。在传统的api项目中,我们要创建各级目录,编写结构体, 定义路由,添加logic文件,这一系列操作,如果按照一条协议的业务需求计算,整个编码下来大概需要5~6分钟才能真正进入业务逻辑的编写, 这还不考虑编写过程中可能产生的各种错误,而随着服务的增多,随着协议的增多,这部分准备工作的时间将成正比上升, 而goctl api则可以完全替代你去做这一部分工作,不管你的协议要定多少个,最终来说,只需要花费10秒不到即可完成。 [!TIP] 其中的结构体编写,路由定义用api进行替代,因此总的来说,省去的是你创建文件夹、添加各种文件及资源依赖的过程的时间。 api命令说明 $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help 从上文中可以看到,根据功能的不同,api包含了很多的自命令和flag,我们这里重点说明一下 go子命令,其功能是生成golang api服务,我们通过goctl api go -h看一下使用帮助: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir 代码输出目录 --api 指定api源文件 --style 指定生成代码文件的文件名称风格,详情见文件名称命名style说明 使用示例 $ goctl api go -api user.api -dir . -style gozero 猜你想看 api语法 api目录 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"rpc命令","keywords":"","body":"rpc命令 Goctl Rpc是goctl脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。 特性 简单易用 快速提升开发效率 出错率低 贴近protoc 快速开始 方式一:快速生成greet服务 通过命令 goctl rpc new ${servieName}生成 如生成greet rpc服务: goctl rpc new greet 执行后代码结构如下: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] 新版本目录详见 rpc目录 方式二:通过指定proto生成rpc服务 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\"./user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } 生成rpc服务代码 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. 准备工作 安装了go环境 安装了protoc & protoc-gen-go,并且已经设置环境变量 更多问题请见 注意事项 用法 rpc服务生成用法 goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 参数说明 --zrpc_out 可选,默认为proto文件所在目录,生成代码的目标目录 --style 可选,输出目录的文件命名风格,详情见https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md --home 可选,指定模板路径 --remote 可选,指定模板远程仓库 --branch 可选,指定模板远程仓库分支,与 --remote 配合使用 你可以理解为 zrpc 代码生成是用 goctl rpc $protoc_command --zrpc_out=${output} 模板,如原来生成 grpc 代码指令为 $ protoc user.proto --go_out=. --go-grpc_out=. 则生成 zrpc 代码指令就为 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out 与 --go-grpc_out 生成的最终目录必须一致 --go_out & --go-grpc_out 和 --zrpc_out 的生成的最终目录必须不为同一目录,否则pb.go和_grpc.pb.go就与main函数同级了,这是不允许的。 --go_out 与 --go-grpc_out 生产的目录会受 --go_opt 和 --grpc-go_opt 和proto源文件中 go_package值的影响,要想理解这里的生成逻辑,建议阅读 官方文文档:Go Generated Code 开发人员需要做什么 关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开发人员仅需要修改 服务中的配置文件编写(etc/xx.json、internal/config/config.go) 服务中业务逻辑编写(internal/logic/xxlogic.go) 服务中资源上下文的编写(internal/svc/servicecontext.go) 注意事项 proto暂不支持多文件同时生成 proto不支持外部依赖包引入,message不支持inline 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有 // Code generated by goctl. DO NOT EDIT! // Source: xxx.proto 的标识,请注意不要在里面写业务性代码;也不要将它写在业务性代码里面。 proto import 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。 proto示例: 错误import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request和return 不支持import } 正确import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// 支持import } message Response { base.Out out = 2;// 支持import } service Greet { rpc Ping(Request) returns(Response); } 猜你想看 rpc目录 rpc配置 rpc调用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-model.html":{"url":"goctl-model.html","title":"model命令","keywords":"","body":"model命令 goctl model 为go-zero下的工具模块中的组件之一,目前支持识别mysql ddl进行model层代码生成,通过命令行或者idea插件(即将支持)可以有选择地生成带redis cache或者不带redis cache的代码逻辑。 快速开始 通过ddl生成 $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c 执行上述命令后即可快速生成CURD代码。 model ├── usermodel.go ├── usermodel_gen.go └── vars.go 通过datasource生成 $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } 用法 $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help 生成规则 默认规则 我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,对于这两个字段生成insert、update时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。 ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 生成代码仅基本的CURD结构。 缓存 对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。 缓存会缓存哪些信息? 对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。 数据有更新(update)操作会清空缓存吗? 会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。 为什么不按照单索引字段生成updateByXxx和deleteByXxx的代码? 理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。 为什么不支持findPageLimit、findAll这种模式代码生成? 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 类型转换规则 mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"plugin命令","keywords":"","body":"plugin命令 goctl支持针对api自定义插件,那我怎么来自定义一个插件了?来看看下面最终怎么使用的一个例子。 $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . 上面这个命令可以分解成如下几步: goctl 解析api文件 goctl 将解析后的结构 ApiSpec 和参数传递给goctl-android可执行文件 goctl-android 根据 ApiSpec 结构体自定义生成逻辑。 此命令前面部分 goctl api plugin -p 是固定参数,goctl-android=\"android -package com.tal\" 是plugin参数,其中goctl-android是插件二进制文件,android -package com.tal是插件的自定义参数,-api user.api -dir .是goctl通用自定义参数。 怎么编写自定义插件? go-zero框架中包含了一个很简单的自定义插件 demo,代码如下: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() 这行代码作用是解析从goctl传递过来的数据,里面包含如下部分内容: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api:定义了api文件的结构数据 Style:可选参数,可以用来控制文件命名规范 Dir:工作目录 完整的基于plugin实现的android plugin演示项目 https://github.com/zeromicro/goctl-android 猜你想看 api目录 api语法 api配置 api命令介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-other.html":{"url":"goctl-other.html","title":"其他命令","keywords":"","body":"其他命令 goctl docker goctl kube goctl docker goctl docker 可以极速生成一个 Dockerfile,帮助开发/运维人员加快部署节奏,降低部署复杂度。 准备工作 docker安装 Dockerfile 额外注意点 选择最简单的镜像:比如alpine,整个镜像5M左右 设置镜像时区RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai 多阶段构建 第一阶段构建出可执行文件,确保构建过程独立于宿主机 第二阶段将第一阶段的输出作为输入,构建出最终的极简镜像 Dockerfile编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 在 greet 项目下创建一个 hello 服务 $ goctl api new hello 文件结构如下: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go 在 hello 目录下一键生成 Dockerfile$ goctl docker -go hello.go Dockerfile 内容如下: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] 在 hello 目录下 build 镜像 $ docker build -t hello:v1 -f service/hello/Dockerfile . 查看镜像 hello v1 5455f2eaea6b 7 minutes ago 18.1MB 可以看出镜像大小约为18M。 启动服务$ docker run --rm -it -p 8888:8888 hello:v1 测试服务$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker总结 goctl 工具极大简化了 Dockerfile 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 goctl kube goctl kube提供了快速生成一个 k8s 部署文件的功能,可以加快开发/运维人员的部署进度,减少部署复杂度。 头疼编写 K8S 部署文件? K8S yaml 参数很多,需要边写边查? 保留回滚版本数怎么设? 如何探测启动成功,如何探活? 如何分配和限制资源? 如何设置时区?否则打印日志是 GMT 标准时间 如何暴露服务供其它服务调用? 如何根据 CPU 和内存使用率来配置水平伸缩? 首先,你需要知道有这些知识点,其次要把这些知识点都搞明白也不容易,再次,每次编写依然容易出错! 创建服务镜像 为了演示,这里我们以 redis:6-alpine 镜像为例。 完整 K8S 部署文件编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 一键生成 K8S 部署文件 $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 生成的 yaml 文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 部署服务,如果 adhoc namespace 不存在的话,请先通过 kubectl create namespace adhoc 创建 $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created 查看服务允许状态 $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s 测试服务$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube 总结 goctl 工具极大简化了 K8S yaml 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 猜你想看 准备工作 api目录 api语法 api配置 api命令介绍 docker介绍 k8s介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-manage.html":{"url":"template-manage.html","title":"模板管理","keywords":"","body":"模板管理 模板操作 自定义模板 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-cmd.html":{"url":"template-cmd.html","title":"模板操作","keywords":"","body":"模板操作 模板(Template)是数据驱动生成的基础,所有的代码(rest api、rpc、model、docker、kube)生成都会依赖模板, 默认情况下,模板生成器会选择内存中的模板进行生成,而对于有模板修改需求的开发者来讲,则需要将模板进行落盘, 从而进行模板修改,在下次代码生成时会加载指定路径下的模板进行生成。 使用帮助 NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help 模板初始化 NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 清除模板 NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 回滚指定分类模板 NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template 回滚模板 NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home 指定模板存储路径 模板加载 在代码生成时可以通过--home来指定模板所在文件夹,目前已支持指定模板目录的命令有: goctl api go 详情可以通过goctl api go --help查看帮助 goctl docker 详情可以通过goctl docker --help查看帮助 goctl kube 详情可以通过goctl kube --help查看帮助 goctl rpc new 详情可以通过goctl rpc new --help查看帮助 goctl rpc proto 详情可以通过goctl rpc proto --help查看帮助 goctl model mysql ddl 详情可以通过goctl model mysql ddl --help查看帮助 goctl model mysql datasource 详情可以通过goctl model mysql datasource --help查看帮助 goctl model pg datasource 详情可以通过goctl model pg datasource --help查看帮助 goctl model mongo 详情可以通过goctl model mongo --help查看帮助 默认情况(在不指定--home)会从$HOME/.goctl目录下读取。 使用示例 初始化模板到指定$HOME/template目录下$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! 使用$HOME/template模板进行greet rpc生成$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template.html":{"url":"template.html","title":"自定义模板","keywords":"","body":"模板修改 场景 实现统一格式的body响应,格式如下: { \"code\": 0, \"msg\": \"OK\", \"data\": {} // ① } ① 实际响应数据 [!TIP] go-zero生成的代码没有对其进行处理 准备工作 我们提前在module为greet的工程下的response包中写一个Response方法,目录树类似如下: greet ├── response │ └── response.go └── xxx... 代码如下 package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } 修改handler模板 $ vim ~/.goctl/api/handler.tpl 将模板替换为以下内容 package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① 替换为你真实的response包名,仅供参考 ② 自定义模板内容 [!TIP] 1.如果本地没有~/.goctl/api/handler.tpl文件,可以通过模板初始化命令goctl template init进行初始化 修改模板前后对比 修改前 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // 以下内容将被自定义模板替换 if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } 修改后 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } 修改模板前后响应体对比 修改前 { \"message\": \"Hello go-zero!\" } 修改后 { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } 总结 本文档仅对http相应为例讲述了自定义模板的流程,除此之外,自定义模板的场景还有: model 层添加kmq model 层生成待有效期option的model实例 http自定义相应格式 ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"extended-reading.html":{"url":"extended-reading.html","title":"扩展阅读","keywords":"","body":"扩展阅读 扩展阅读是对go-zero 中的最佳实现和组件的介绍, 因此会比较庞大,而此资源将会持续更新,也欢迎大家来进行文档贡献,本节将包含以下目录(按照文档更新时间排序): 快速构建高并发微服务 日志组件介绍 布隆过滤器 executors 流处理组件 fx go-zero mysql使用介绍 redis锁 periodlimit限流 令牌桶限流 时间轮介绍 熔断原理与实现 进程内缓存组件 collection.Cache 高效的关键词替换和敏感词过滤工具 服务自适应降载保护设计 文本序列化和反序列化 并发处理工具 MapReduce 基于prometheus的微服务指标监控 防止缓存击穿之进程内共享调用 DB缓存机制 zrpc 使用介绍 go-zero缓存设计之持久层缓存 go-zero缓存设计之业务层缓存 go-zero分布式定时任务 我是如何用go-zero 实现一个中台系统 流数据处理利器 10月3日线上交流问题汇总 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"logx.html":{"url":"logx.html","title":"日志组件介绍","keywords":"","body":"logx 使用示例 var c logx.LogConf // 从 yaml 文件中 初始化配置 conf.MustLoad(\"config.yaml\", &c) // logx 根据配置初始化 logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() 初始化 logx 有很多可以配置项,可以参考 logx.LogConf 中的定义。目前可以使用 logx.MustSetUp(c) 进行初始化配置,如果没有进行初始化配置,所有的配置将使用默认配置。 Level logx 支持的打印日志级别有: alert info error severe fatal slow stat 可以使用对应的方法打印出对应级别的日志。 同时为了方便调试,线上使用,可以动态调整日志打印级别,其中可以通过 logx.SetLevel(uint32) 进行级别设置,也可以通过配置初始化进行设置。目前支持的参数为: const ( // 打印所有级别的日志 InfoLevel = iota // 打印 errors, slows, stacks 日志 ErrorLevel // 仅打印 severe 级别日志 SevereLevel ) 日志模式 目前日志打印模式主要分为2种,一种文件输出,一种控制台输出。推荐方式,当采用 k8s,docker 等部署方式的时候,可以将日志输出到控制台,使用日志收集器收集导入至 es 进行日志分析。如果是直接部署方式,可以采用文件输出方式,logx 会自动在指定文件目录创建对应 5 个对应级别的的日志文件保存日志。 . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log 同时会按照自然日进行文件分割,当超过指定配置天数,会对日志文件进行自动删除,打包等操作。 禁用日志 如果不需要日志打印,可以使用 logx.Close() 关闭日志输出。注意,当禁用日志输出,将无法在次打开,具体可以参考 logx.RotateLogger 和 logx.DailyRotateRule 的实现。 关闭日志 因为 logx 采用异步进行日志输出,如果没有正常关闭日志,可能会造成部分日志丢失的情况。必须在程序退出的地方关闭日志输出: logx.Close() 框架中 rest 和 zrpc 等大部分地方已经做好了日志配置和关闭相关操作,用户可以不用关心。 同时注意,当关闭日志输出之后,将无法在次打印日志了。 推荐写法: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration 我们打印日志的时候可能需要打印耗时情况,可以使用 logx.WithDuration(time.Duration), 参考如下示例: startTime := timex.Now() // 数据库查询 rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } 会输出如下格式 {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} 这样就可以很容易统计出慢 sql 相关信息。 TraceLog tracingEntry 是为了链路追踪日志输出定制的。可以打印 context 中的 traceId 和 spanId 信息,配合我们的 rest 和 zrpc 很容易完成链路日志的相关打印。示例如下 logx.WithContext(context.Context).Info(\"This is info!\") SysLog 应用中可能有部分采用系统 log 进行日志打印,logx 同样封装方法,很容易将 log 相关的日志收集到 logx 中来。 logx.CollectSysLog() 日志配置相关 LogConf 定义日志系统所需的基本配置 完整定义如下: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode 定义了日志打印的方式。默认的模式是 console, 打印到控制台上面。 目前支持的模式如下: console 打印到控制台 file 打印到指定路径下的access.log, error.log, stat.log等文件里 volume 为了在k8s内打印到mount进来的存储上,因为多个pod可能会覆盖相同的文件,volume模式自动识别pod并按照pod分开写各自的日志文件 Path Path 定义了文件日志的输出路径,默认值为 logs。 Level Level 定义了日志打印级别,默认值为 info。 目前支持的级别如下: info error severe Compress Compress 定义了日志是否需要压缩,默认值为 false。在 Mode 为 file 模式下面,文件最后会进行打包压缩成 .gz 文件。 KeepDays KeepDays 定义日志最大保留天数,默认值为 0,表示不会删除旧的日志。在 Mode 为 file 模式下面,如果超过了最大保留天数,旧的日志文件将会被删除。 StackCooldownMillis StackCooldownMillis 定义了日志输出间隔,默认为 100 毫秒。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"bloom.html":{"url":"bloom.html","title":"布隆过滤器","keywords":"","body":"bloom go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 布隆过滤器bloom 在做服务器开发的时候,相信大家有听过布隆过滤器,可以判断某元素在不在集合里面,因为存在一定的误判和删除复杂问题,一般的使用场景是:防止缓存击穿(防止恶意攻击)、 垃圾邮箱过滤、cache digests 、模型检测器等、判断是否存在某行数据,用以减少对磁盘访问,提高服务的访问性能。 go-zero 提供的简单的缓存封装 bloom.bloom,简单使用方式如下 // 初始化 redisBitSet store := redis.New(\"redis 地址\", func(r *redis.Redis) { r.Type = redis.NodeType }) // 声明一个bitSet, key=\"test_key\"名且bits是1024位 bitSet := newRedisBitSet(store, \"test_key\", 1024) // 判断第0位bit存不存在 isSetBefore, err := bitSet.check([]uint{0}) // 对第512位设置为1 err = bitSet.set([]uint{512}) // 3600秒后过期 err = bitSet.expire(3600) // 删除该bitSet err = bitSet.del() bloom 简单介绍了最基本的redis bitset 的使用。下面是真正的bloom实现。 对元素hash 定位 // 对元素进行hash 14次(const maps=14),每次都在元素后追加byte(0-13),然后进行hash. // 将locations[0-13] 进行取模,最终返回locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i 向bloom里面add 元素 // 我们可以发现 add方法使用了getLocations和bitSet的set方法。 // 我们将元素进行hash成长度14的uint切片,然后进行set操作存到redis的bitSet里面。 func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } 检查bloom里面是否有某元素 // 我们可以发现 Exists方法使用了getLocations和bitSet的check方法 // 我们将元素进行hash成长度14的uint切片,然后进行bitSet的check验证,存在返回true,不存在或者check失败返回false func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } 本节主要介绍了go-zero框架中的 core.bloom 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors 在 go-zero 中,executors 充当任务池,做多任务缓冲,适用于做批量处理的任务。如:clickhouse 大批量 insert,sql batch insert。同时也可以在 go-queue 中看到 executors 【在 queue 里面使用的是 ChunkExecutor ,限定任务提交字节大小】。 所以当你存在以下需求,都可以使用这个组件: 批量提交任务 缓冲一部分任务,惰性提交 延迟任务提交 具体解释之前,先给一个大致的概览图: 接口设计 在 executors 包下,有如下几个 executor : Name Margin value bulkexecutor 达到 maxTasks 【最大任务数】 提交 chunkexecutor 达到 maxChunkSize【最大字节数】提交 periodicalexecutor basic executor delayexecutor 延迟执行传入的 fn() lessexecutor 你会看到除了有特殊功能的 delay,less ,其余 3 个都是 executor + container 的组合设计: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // 选项模式:在 go-zero 中多处出现。在多配置下,比较好的设计思路 // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute 真正做执行的函数] [maxTasks 执行临界点] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. 可以看出 bulkexecutor 底层依赖 periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } 而这个 container是个 interface: TaskContainer interface { // 把 task 加入 container AddTask(task interface{}) bool // 实际上是去执行传入的 execute func() Execute(tasks interface{}) // 达到临界值,移除 container 中全部的 task,通过 channel 传递到 execute func() 执行 RemoveAll() interface{} } 由此可见之间的依赖关系: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] 所以你想完成自己的 executor,可以实现 container 的这 3 个接口,再结合 periodicalexecutor 就行 所以回到👆那张图,我们的重点就放在 periodicalexecutor,看看它是怎么设计的? 如何使用 首先看看如何在业务中使用这个组件: 现有一个定时服务,每天固定时间去执行从 mysql 到 clickhouse 的数据同步: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } 初始化 bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() 是真正insert执行函数【需要开发者自己编写具体业务逻辑】 dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // 3s会自动刷一次container中task去执行 executors.WithBulkTasks(10240), // container最大task数。一般设为2的幂次 ) } [!TIP] 额外介绍一下:clickhouse 适合大批量的插入,因为 insert 速度很快,大批量 insert 更能充分利用 clickhouse 主体业务逻辑编写: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] 可能会疑惑为什么要 Flush(), Wait() ,后面会通过源码解析一下 使用上总体分为 3 步: Add():加入 task Flush():刷新 container 中的 task Wait():等待全部 task 执行完成 源码分析 [!TIP] 此处主要分析 periodicalexecutor,因为其他两个常用的 executor 都依赖它 初始化 func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander:传递 tasks 的 channel container:暂存 Add() 的 task confirmChan:阻塞 Add() ,在开始本次的 executeTasks() 会放开阻塞 ticker:定时器,防止 Add() 阻塞时,会有一个定时执行的机会,及时释放暂存的 task Add() 初始化完,在业务逻辑的第一步就是把 task 加入 executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask 将container中tasks pop, return if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } addAndCheck() 中 AddTask() 就是在控制最大 tasks 数,如果超过就执行 RemoveAll() ,将暂存 container 的 tasks pop,传递给 commander ,后面有 goroutine 循环读取,然后去执行 tasks。 backgroundFlush() 开启一个后台协程,对 container 中的 task,不断刷新: func (pe *PeriodicalExecutor) backgroundFlush() { // 封装 go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // 从channel拿到 []tasks case vals := pe.interval*idleRound { // 既没到maxTask,Flush() err,并且 last->now 时间过长,会再次触发 Flush() // 只有这置反,才会开启一个新的 backgroundFlush() 后台协程 pe.guarded = false // 再次刷新,防止漏掉 pe.Flush() return } } } }) } 总体两个过程: commander 接收到 RemoveAll() 传递来的 tasks,然后执行,并放开 Add() 的阻塞,得以继续 Add() ticker 到时间了,如果第一步没有执行,则自动 Flush() ,也会去做 task 的执行 Wait() 在 backgroundFlush() ,提到一个函数:enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } 这样列举就知道为什么之前在最后要带上 dts.insertExecutor.Wait(),当然要等待全部的 goroutine task 完成。 思考 在看源码中,思考了一些其他设计上的思路,大家是否也有类似的问题: 在分析 executors 中,会发现很多地方都有 lock [!TIP] go test 存在竞态,使用加锁来避免这种情况 在分析 confirmChan 时发现,confirmChan 在此次提交才出现,为什么会这么设计? 之前是:wg.Add(1) 是写在 executeTasks() ;现在是:先wg.Add(1),再放开 confirmChan 阻塞 如果 executor func 执行阻塞,Add task 还在进行,因为没有阻塞,可能很快执行到 Executor.Wait(),这时就会出现 wg.Wait() 在 wg.Add() 前执行,这会 panic 具体可以看最新版本的TestPeriodicalExecutor_WaitFast() ,不妨跑在此版本上,就可以重现 总结 剩余还有几个 executors 的分析,就留给大家去看看源码。 总之,整体设计上: 遵循面向接口设计 灵活使用 channel ,waitgroup 等并发工具 执行单元+存储单元的搭配使用 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"fx.html":{"url":"fx.html","title":"流处理组件 fx","keywords":"","body":"数据的流处理利器 流处理(Stream processing)是一种计算机编程范式,其允许给定一个数据序列(流处理数据源),一系列数据操作(函数)被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到Kafka,然后再由Job消费Kafaka写到elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx gozero是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce工具进行处理。 总结 本篇文章介绍了流处理的基本概念和gozero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mysql.html":{"url":"mysql.html","title":"go-zero mysql使用介绍","keywords":"","body":"mysql go-zero 提供更易于操作的 mysql API。 [!TIP] 但是 stores/mysql 定位不是一个 orm 框架,如果你需要通过 sql/scheme -> model/struct 逆向生成 model 层代码,可以使用「goctl model」,这个是极好的功能。 Feature 相比原生,提供对开发者更友好的 API 完成 queryField -> struct 的自动赋值 批量插入「bulkinserter」 自带熔断 API 经过若干个服务的不断考验 提供 partial assignment 特性,不强制 struct 的严格赋值 Connection 下面用一个例子简单说明一下如何创建一个 mysql 连接的 model: // 1. 快速连接一个 mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. 在 servicecontext 中调用,懂model上层的logic层调用 model.NewMysqlModel(heraMysql, tablename), // 3. model层 mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. 创建一个批量insert的 [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD 准备一个 User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` // 头像 UserName string `db:\"user_name\"` // 姓名 Sex int `db:\"sex\"` // 1男,2女 MobilePhone string `db:\"mobile_phone\"` // 手机号 } 其中 userBuilderQueryRows : go-zero 中提供 struct -> [field...] 的转化,开发者可以将此当成模版直接使用。 insert // 一个实际的insert model层操作 func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } 拼接 insertsql 将 insertsql 以及占位符对应的 struct field 传入 -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...) : args... 需对应 sql 中的占位符。不然会出现赋值异常的问题。 go-zero 将涉及 mysql 修改的操作统一抽象为 Exec() 。所以 insert/update/delete 操作本质上是一致的。其余两个操作,开发者按照上述 insert 流程尝试即可。 query 只需要传入 querysql 和 model 结构体,就可以获取到被赋值好的 model 。无需开发者手动赋值。 func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } 声明 model struct ,拼接 querysql conn.QueryRow(&model, querysql, args...) : args... 与 querysql 中的占位符对应。 [!WARNING] QueryRow() 中第一个参数需要传入 Ptr 「底层需要反射对 struct 进行赋值」 上述是查询一条记录,如果需要查询多条记录时,可以使用 conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } 与 QueryRow() 不同的地方在于: model 需要设置成 Slice ,因为是查询多行,需要对多个 model 赋值。但同时需要注意️:第一个参数需要传入 Ptr querypartial 从使用上,与上述的 QueryRow() 无异「这正体现了 go-zero 高度的抽象设计」。 区别: QueryRow() : len(querysql fields) == len(struct) ,且一一对应 QueryRowPartial() :len(querysql fields) numA:数据库字段数;numB:定义的 struct 属性数。 如果 numA ,但是你恰恰又需要统一多处的查询时「定义了多个 struct 返回不同的用途,恰恰都可以使用相同的 querysql 」,就可以使用 QueryRowPartial() 事务 要在事务中执行一系列操作,一般流程如下: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // 返回任何错误都会回滚事务 if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // 还可以继续执行 insert/update/delete 相关操作 return nil }) 如同上述例子,开发者只需将 事务 中的操作都包装在一个函数 func(session sqlx.Session) error {} 中即可,如果事务中的操作返回任何错误, Transact() 都会自动回滚事务。 分布式事务 go-zero 与 dtm 深度合作,原生的支持了分布式事务,详情参见 分布式事务支持 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis锁","keywords":"","body":"redis-lock redis lock 既然是锁,首先想到的一个作用就是:防重复点击,在一个时间点只有一个请求产生效果。 而既然是 redis,就得具有排他性,同时也具有锁的一些共性: 高性能 不能出现死锁 不能出现节点down掉后加锁失败 go-zero 中利用 redis set key nx 可以保证key不存在时写入成功,px 可以让key超时后自动删除「最坏情况也就是超时自动删除key,从而也不会出现死锁」 example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. 可选操作,设置 redislock 过期时间 redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"当前有其他用户正在进行操作,请稍后重试\") } defer func() { recover() // 3. 释放锁 redisLock.Release() }() 和你在使用 sync.Mutex 的方式时一致的。加锁解锁,执行你的业务操作。 获取锁 lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } 先介绍几个 redis 的命令选项,以下是为 set 命令增加的选项: ex seconds :设置key过期时间,单位s px milliseconds :设置key过期时间,单位毫秒 nx:key不存在时,设置key的值 xx:key存在时,才会去设置key的值 其中 lua script 涉及的入参: args 示例 含义 KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD 唯一标识:随机字符串 ARGV[2] 30000 设置锁的过期时间 然后来说说代码特性: Lua 脚本保证原子性「当然,把多个操作在 Redis 中实现成一个操作,也就是单命令操作」 使用了 set key value px milliseconds nx value 具有唯一性 加锁时首先判断 key 的 value 是否和之前设置的一致,一致则修改过期时间 释放锁 delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } 释放锁的时候只需要关注一点: 不能释放别人的锁,不能释放别人的锁,不能释放别人的锁 所以需要先 get(key) == value「key」,为 true 才会去 delete Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit限流","keywords":"","body":"periodlimit 不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性。即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪。 本文就来介绍一下 periodlimit 。 使用 const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero 采取 滑动窗口 计数的方式,计算一段时间内对同一个资源的访问次数,如果超过指定的 limit ,则拒绝访问。当然如果你是在一段时间内访问不同的资源,每一个资源访问量都不超过 limit ,此种情况是允许大量请求进来的。 而在一个分布式系统中,存在多个微服务提供服务。所以当瞬间的流量同时访问同一个资源,如何让计数器在分布式系统中正常计数? 同时在计算资源访问时,可能会涉及多个计算,如何保证计算的原子性? go-zero 借助 redis 的 incrby 做资源访问计数 采用 lua script 做整个窗口计算,保证计算的原子性 下面来看看 lua script 控制的几个关键属性: argument mean key[1] 访问资源的标示 ARGV[1] limit => 请求总数,超过则限速。可设置为 QPS ARGV[2] window大小 => 滑动窗口,用 ttl 模拟出滑动的效果 -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- 如果是第一次访问,设置过期时间 => TTL = window size -- 因为是只限制一段时间的访问次数 if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current 至于上述的 return code ,返回给调用方。由调用方来决定请求后续的操作: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit 下面这张图描述了请求进入的过程,以及请求触发 limit 时后续发生的情况: 后续处理 如果在服务某个时间点,请求大批量打进来,periodlimit 短期时间内达到 limit 阈值,而且设置的时间范围还远远没有到达。后续请求的处理就成为问题。 periodlimit 中并没有处理,而是返回 code 。把后续请求的处理交给了开发者自己处理。 如果不做处理,那就是简单的将请求拒绝 如果需要处理这些请求,开发者可以借助 mq 将请求缓冲,减缓请求的压力 采用 tokenlimit,允许暂时的流量冲击 所以下一篇我们就来聊聊 tokenlimit 总结 go-zero 中的 periodlimit 限流方案是基于 redis 计数器,通过调用 redis lua script ,保证计数过程的原子性,同时保证在分布式的情况下计数是正常的。但是这种方案存在缺点,因为它要记录时间窗口内的所有行为记录,如果这个量特别大的时候,内存消耗会变得非常严重。 参考 go-zero periodlimit 分布式服务限流实战,已经为你排好坑了 tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"令牌桶限流","keywords":"","body":"tokenlimit 本节将通过令牌桶限流(tokenlimit)来介绍其基本使用。 使用 const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit 从整体上令牌桶生产token逻辑如下: 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中; 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃; 当流量以速率v进入,从桶中以速率v取令牌,拿到令牌的流量通过,拿不到令牌流量不通过,执行熔断逻辑; go-zero 在两类限流器下都采取 lua script 的方式,依赖redis可以做到分布式限流,lua script同时可以做到对 token 生产读取操作的原子性。 下面来看看 lua script 控制的几个关键属性: argument mean ARGV[1] rate 「每秒生成几个令牌」 ARGV[2] burst 「令牌桶最大值」 ARGV[3] now_time「当前时间戳」 ARGV[4] get token nums 「开发者需要获取的token数」 KEYS[1] 表示资源的tokenkey KEYS[2] 表示刷新时间的key -- 返回是否可以活获得预期的token local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:需要填满 token_bucket 需要多久 local fill_time = capacity/rate -- 将填充时间向下取整 local ttl = math.floor(fill_time*2) -- 获取目前 token_bucket 中剩余 token 数 -- 如果是第一次进入,则设置 token_bucket 数量为 令牌桶最大值 local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- 上一次更新 token_bucket 的时间 local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- 通过当前时间与上一次更新时间的跨度,以及生产token的速率,计算出新的token数 -- 如果超过 max_burst,多余生产的token会被丢弃 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- 更新新的token数,以及更新时间 redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed 上述可以看出 lua script :只涉及对 token 操作,保证 token 生产合理和读取合理。 函数分析 从上述流程中看出: 有多重保障机制,保证限流一定会完成。 如果redis limiter失效,至少在进程内rate limiter兜底。 重试 redis limiter 机制保证尽可能地正常运行。 总结 go-zero 中的 tokenlimit 限流方案适用于瞬时流量冲击,现实请求场景并不以恒定的速率。令牌桶相当预请求,当真实的请求到达不至于瞬间被打垮。当流量冲击到一定程度,则才会按照预定速率进行消费。 但是生产token上,不能按照当时的流量情况作出动态调整,不够灵活,还可以进行进一步优化。此外可以参考Token bucket WIKI 中提到分层令牌桶,根据不同的流量带宽,分至不同排队中。 参考 go-zero tokenlimit Go-Redis 提供的分布式限流库 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"时间轮介绍","keywords":"","body":"TimingWheel 本文来介绍 go-zero 中 延迟操作。延迟操作,可以采用两个方案: Timer:定时器维护一个优先队列,到时间点执行,然后把需要执行的 task 存储在 map 中 collection 中的 timingWheel ,维护一个存放任务组的数组,每一个槽都维护一个存储task的双向链表。开始执行时,计时器每隔指定时间执行一个槽里面的tasks。 方案2把维护task从 优先队列 O(nlog(n)) 降到 双向链表 O(1),而执行task也只要轮询一个时间点的tasks O(N),不需要像优先队列,放入和删除元素 O(nlog(n))。 cache 中的 timingWheel 首先我们先来在 collection 的 cache 中关于 timingWheel 的使用: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel 这是 cache 初始化中也同时初始化 timingWheel 做key的过期处理,参数依次代表: interval:时间划分刻度 numSlots:时间槽 execute:时间点执行函数 在 cache 中执行函数则是 删除过期key,而这个过期则由 timingWheel 来控制推进时间。 接下来,就通过 cache 对 timingWheel 的使用来认识。 初始化 // 真正做初始化 func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // 单个时间格时间间隔 ticker: ticker, // 定时器,做时间推动,以interval为单位推进 slots: make([]*list.List, numSlots), // 时间轮 timers: NewSafeMap(), // 存储task{key, value}的map [执行execute所需要的参数] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // 执行函数 numSlots: numSlots, // 初始化 slots num setChannel: make(chan timingEntry), // 以下几个channel是做task传递的 moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // 把 slot 中存储的 list 全部准备好 tw.initSlots() // 开启异步协程,使用 channel 来做task通信和传递 go tw.run() return tw, nil } 以上比较直观展示 timingWheel 的 “时间轮”,后面会围绕这张图解释其中推进的细节。 go tw.run() 开一个协程做时间推动: func (tw *TimingWheel) run() { for { select { // 定时器做时间推动 -> scanAndRunTasks() case 可以看出,在初始化的时候就开始了 timer 执行,并以interval时间段转动,然后底层不停的获取来自 slot 中的 list 的task,交给 execute 执行。 Task Operation 紧接着就是设置 cache key : func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } 先看在 data map 中有没有存在这个key 存在,则更新 expire -> MoveTimer() 第一次设置key -> SetTimer() 所以对于 timingWheel 的使用上就清晰了,开发者根据需求可以 add 或是 update。 同时我们跟源码进去会发现:SetTimer() MoveTimer() 都是将task输送到channel,由 run() 中开启的协程不断取出 channel 的task操作。 SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() 由上面的调用链,有一个都会调用的函数:moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => 通过key获取 [positionEntry「pos, task」] val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay 延迟时间比一个时间格间隔还小,没有更小的刻度,说明任务应该立即执行 if task.delay interval,则通过 延迟时间delay 计算其出时间轮中的 new pos, circle pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // 记录前后的移动offset。为了后面过程重新入队 timer.item.diff = pos - timer.pos } else if circle > 0 { // 转移到下一层,将 circle 转换为 diff 一部分 circle-- timer.item.circle = circle // 因为是一个数组,要加上 numSlots [也就是相当于要走到下一层] timer.item.diff = tw.numSlots + pos - timer.pos } else { // 如果 offset 提前了,此时 task 也还在第一层 // 标记删除老的 task,并重新入队,等待被执行 timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } 以上过程有以下几种情况: delay :因为 针对改变的 delay: new >= old: newCircle > 0:计算diff,并将 circle 转换为 下一层,故diff + numslots 如果只是单纯延迟时间缩短,则将老的task标记删除,重新加入list,等待下一轮loop被execute Execute 之前在初始化中,run() 中定时器的不断推进,推进的过程主要就是把 list中的 task 传给执行的 execute func。我们从定时器的执行开始看: // 定时器 「每隔 interval 会执行一次」 func (tw *TimingWheel) onTick() { // 每次执行更新一下当前执行 tick 位置 tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // 获取此时 tick位置 中的存储task的双向链表 l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } 紧接着是如何去执行 execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // 存储目前需要执行的task{key, value} [execute所需要的参数,依次传递给execute执行] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // 标记删除,在 scan 中做真正的删除 「删除map的data」 if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // 当前执行点已经过期,但是同时不在第一层,所以当前层即然已经完成了,就会降到下一层 // 但是并没有修改 pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // 因为之前已经标注了diff,需要再进入队列 next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // 以上的情况都是不能执行的情况,能够执行的会被加入tasks中 tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks,然后把每个 task->execute 执行即可 tw.runTasks(tasks) } 具体的分支情况在注释中说明了,在看的时候可以和前面的 moveTask() 结合起来,其中 circle 下降,diff 的计算是关联两个函数的重点。 至于 diff 计算就涉及到 pos, circle 的计算: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } 上面的过程可以简化成下面: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots 总结 timingWheel 靠定时器推动,时间前进的同时会取出当前时间格中 list「双向链表」的task,传递到 execute 中执行。 而时间分隔上,时间轮有 circle 分层,这样就可以不断复用原有的 numSlots ,因为定时器在不断 loop,而执行可以把上层的 slot 下降到下层,在不断 loop 中就可以执行到上层的task。 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"eco.html":{"url":"eco.html","title":"go-zero 生态","keywords":"","body":"go-zero 生态 工具中心 intellij插件 vscode插件 分布式事务支持 插件中心 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tool-center.html":{"url":"tool-center.html","title":"工具中心","keywords":"","body":"工具中心 在go-zero中,提供了很多提高工程效率的工具,如api,rpc生成,在此基础之上,api文件的编写就显得那么的无力, 因为缺少了高亮,代码提示,模板生成等,本节将带你了解go-zero是怎么解决这些难题的,本节包含以下小节: intellij插件 vscode插件 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"intellij.html":{"url":"intellij.html","title":"intellij插件","keywords":"","body":"intellij插件 Go-Zero Plugin 介绍 一款支持go-zero api语言结构语法高亮、检测以及api、rpc、model快捷生成的插件工具。 idea版本要求 IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ 版本特性 api语法高亮 api语法、语义检测 struct、route、handler重复定义检测 type跳转到类型声明位置 上下文菜单中支持api、rpc、mode相关menu选项 代码格式化(option+command+L) 代码提示 安装方式 方式一 在github的release中找到最新的zip包,下载本地安装即可。(无需解压) 方式二 在plugin商店中,搜索Goctl安装即可 预览 新建 Api(Proto) file 在工程区域目标文件夹右键->New-> New Api(Proto) File ->Empty File/Api(Proto) Template,如图: 快速生成api/rpc服务 在目标文件夹右键->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code生成 方法一(工程区域) 对应文件(api、proto、sql)右键->New->Go Zero-> Api/Rpc/Model Code,如图: 方法二(编辑区域) 对应文件(api、proto、sql)右键-> Generate-> Api/Rpc/Model Code 错误提示 Live Template Live Template可以加快我们对api文件的编写,比如我们在go文件中输入main关键字根据tip回车后会插入一段模板代码 func main(){ } 或者说看到下图你会更加熟悉,曾几何时你还在这里定义过template 下面就进入今天api语法中的模板使用说明吧,我们先来看看service模板的效果 首先上一张图了解一下api文件中几个模板生效区域(psiTree元素区域) 预设模板及生效区域 模板关键字 psiTree生效区域 描述 @doc ApiService doc注释模板 doc ApiService doc注释模板 struct Struct struct声明模板 info ApiFile info block模板 type ApiFile type group模板 handler ApiService handler文件名模板 get ApiService get方法路由模板 head ApiService head方法路由模板 post ApiService post方法路由模板 put ApiService put方法路由模板 delete ApiService delete方法路由模板 connect ApiService connect方法路由模板 options ApiService options方法路由模板 trace ApiService trace方法路由模板 service ApiFile service服务block模板 json Tag、Tag literal tag模板 xml Tag、Tag literal tag模板 path Tag、Tag literal tag模板 form Tag、Tag literal tag模板 关于每个模板对应内容可在Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags中查看详细模板内容,如json tag模板内容为 json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"vscode.html":{"url":"vscode.html","title":"vscode插件","keywords":"","body":"vs code 插件 该插件可以安装在 1.46.0+ 版本的 Visual Studio Code 上,首先请确保你的 Visual Studio Code 版本符合要求,并已安装 goctl 命令行工具。如果尚未安装 Visual Studio Code,请安装并打开 Visual Studio Code。 导航到“扩展”窗格,搜索 goctl 并安装此扩展(发布者ID为 “xiaoxin-technology.goctl”)。 Visual Studio Code 扩展使用请参考这里。 功能列表 已实现功能 语法高亮 跳转到定义/引用 代码格式化 代码块提示 未实现功能: 语法错误检查 跨文件代码跳转 goctl 命令行调用 语法高亮 代码跳转 代码格式化 调用 goctl 命令行格式化工具,使用前请确认 goctl 已加入 $PATH 且有可执行权限 代码块提示 info 代码块 type 代码块 service 代码块 handler 代码块 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"分布式事务支持","keywords":"","body":"分布式事务支持 需求场景 在微服务架构中,当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多: 订单系统:需要保证创建订单和扣减库存要么同时成功,要么同时回滚 跨行转账场景:数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败 积分兑换场景:需要保证积分扣减和权益增加同时成功,或者同时失败 出行订票场景:需要在第三方系统同时定几张票,要么同时成功,要么全部取消 面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 解决方案 go-zero与dtm强强联合,推出了在go-zero中无缝接入dtm的极简方案,是go生态中首家提供分布式事务能力的微服务框架。该方案具备以下特征: dtm服务可以通过配置,直接注册到go-zero的注册中心 go-zero能够以内建的target格式访问dtm服务器 dtm能够识别go-zero的target格式,动态访问go-zero中的服务 详细的接入方式,参见dtm文档:go-zero支持 更多应用场景 dtm不仅可以解决上述的分布式事务场景,还可以解决更多的与数据一致性相关的场景,包括: 数据库与缓存一致性: dtm 的二阶段消息,能够保证数据库更新操作,和缓存更新/删除操作的原子性 秒杀系统: dtm 能够保证秒杀场景下,创建的订单量与库存扣减数量完全一样,无需后续的人工校准 多种存储组合: dtm 已支持数据库、Redis、Mongo等多种存储,可以将它们组合为一个全局事务,保证数据的一致性 更多 dtm 的能力和介绍,参见dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"plugin-center.html":{"url":"plugin-center.html","title":"插件中心","keywords":"","body":"插件中心 goctl api提供了对plugin命令来支持对api进行功能扩展,当goctl api中的功能不满足你的使用, 或者需要对goctl api进行功能自定义的扩展,那么插件功能将非常适合开发人员进行自给自足,详情见 goctl plugin 插件资源 goctl-go-compact goctl默认的一个路由一个文件合并成一个文件 goctl-swagger 通过api文件生成swagger文档 goctl-php goctl-php是一款基于goctl的插件,用于生成 php 调用端(服务端) http server请求代码 猜你想看 goctl插件 api语法介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"learning-resource.html":{"url":"learning-resource.html","title":"学习资源","keywords":"","body":"学习资源 这里将不定期更新go-zero的最新学习资源通道,目前包含通道有: 公众号 Go夜读 Go开源说 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"wechat.html":{"url":"wechat.html","title":"公众号","keywords":"","body":"公众号 微服务实践是go-zero的官方公众号,在这里会发布最新的go-zero最佳实践,同步go夜读、go开源说、GopherChina、腾讯云开发者大会等多渠道关于go-zero的最新技术和资讯。 公众号名称 公众号作者 公众号二维码 微服务实践 kevwan 推荐主题 《带你十天轻松搞定 Go 微服务》 该系列将带你在本机利用 go-zero 快速开发一个商城系统,向大家详细展示了基于 go-zero 框架构建微服务的过程,整个系列分十篇文章,目录结构如下: 第一天:环境搭建 第二天:服务拆分 第三天:用户服务 第四天:产品服务 第五天:订单服务 第六天:支付服务 第七天:RPC服务Auth验证 第八天:服务监控 第九天:链路追踪 第十天:分布式事务 干货 这里列举一些干货,想要收获更多go-zero最佳实践干货,可以关注公众号获取最新动态。 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goreading.html":{"url":"goreading.html","title":"Go夜读","keywords":"","body":"Go夜读 2020-08-16 晓黑板 go-zero 微服务框架的架构设计 2020-10-03 go-zero 微服务框架和线上交流 防止缓存击穿之进程内共享调用 基于go-zero实现JWT认证 再见go-micro!企业项目迁移go-zero全攻略(一) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gotalk.html":{"url":"gotalk.html","title":"Go开源说","keywords":"","body":"Go开源说 Go 开源说第四期 - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-cache.html":{"url":"redis-cache.html","title":"go-zero缓存设计之持久层缓存","keywords":"","body":"go-zero缓存设计之持久层缓存 缓存设计原理 我们对缓存是只删除,不做更新,一旦DB里数据出现修改,我们就会直接删除对应的缓存,而不是去更新。 我们看看删除缓存的顺序怎样才是正确的。 先删除缓存,再更新DB 我们看两个并发请求的情况,A请求需要更新数据,先删除了缓存,然后B请求来读取数据,此时缓存没有数据,就会从DB加载数据并写回缓存,然后A更新了DB,那么此时缓存内的数据就会一直是脏数据,直到缓存过期或者有新的更新数据的请求。如图 先更新DB,再删除缓存 A请求先更新DB,然后B请求来读取数据,此时返回的是老数据,此时可以认为是A请求还没更新完,最终一致性,可以接受,然后A删除了缓存,后续请求都会拿到最新数据,如图 让我们再来看一下正常的请求流程: 第一个请求更新DB,并删除了缓存 第二个请求读取缓存,没有数据,就从DB读取数据,并回写到缓存里 后续读请求都可以直接从缓存读取 我们再看一下DB查询有哪些情况,假设行记录里有ABCDEFG七列数据: 只查询部分列数据的请求,比如请求其中的ABC,CDE或者EFG等,如图 查询单条完整行记录,如图 查询多条行记录的部分或全部列,如图 对于上面三种情况,首先,我们不用部分查询,因为部分查询没法缓存,一旦缓存了,数据有更新,没法定位到有哪些数据需要删除;其次,对于多行的查询,根据实际场景和需要,我们会在业务层建立对应的从查询条件到主键的映射;而对于单行完整记录的查询,go-zero 内置了完整的缓存管理方式。所以核心原则是:go-zero 缓存的一定是完整的行记录。 下面我们来详细介绍 go-zero 内置的三种场景的缓存处理方式: 基于主键的缓存PRIMARY KEY (`id`) 这种相对来讲是最容易处理的缓存,只需要在 redis 里用 primary key 作为 key 来缓存行记录即可。 基于唯一索引的缓存 在做基于索引的缓存设计的时候我借鉴了 database 索引的设计方法,在 database 设计里,如果通过索引去查数据时,引擎会先在 索引->主键 的 tree 里面查找到主键,然后再通过主键去查询行记录,就是引入了一个间接层去解决索引到行记录的对应问题。在 go-zero 的缓存设计里也是同样的原理。 基于索引的缓存又分为单列唯一索引和多列唯一索引: 但是对于 go-zero 来说,单列和多列只是生成缓存 key 的方式不同而已,背后的控制逻辑是一样的。然后 go-zero 内置的缓存管理就比较好的控制了数据一致性问题,同时也内置防止了缓存的击穿、穿透、雪崩问题(这些在 gopherchina 大会上分享的时候仔细讲过,见后续 gopherchina 分享视频)。 另外,go-zero 内置了缓存访问量、访问命中率统计,如下所示: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 可以看到比较详细的统计信息,便于我们来分析缓存的使用情况,对于缓存命中率极低或者请求量极小的情况,我们就可以去掉缓存了,这样也可以降低成本。 单列唯一索引如下: UNIQUE KEY `product_idx` (`product`) 多列唯一索引如下: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) 缓存代码解读 1.基于主键的缓存逻辑 具体实现代码如下: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } 这里的 Take 方法是先从缓存里去通过 key 拿数据,如果拿到就直接返回,如果拿不到,那么就通过 query 方法去 DB 读取完整行记录并写回缓存,然后再返回数据。整个逻辑还是比较简单易懂的。 我们详细看看 Take 的实现: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } Take 的逻辑如下: 用 key 从缓存里查找数据 如果找到,则返回数据 如果找不到,用 query 方法去读取数据 读到后调用 c.SetCache(key, v) 设置缓存 其中的 doTake 代码和解释如下: // v - 需要读取的数据对象 // key - 缓存key // query - 用来从DB读取完整数据的方法 // cacheVal - 用来写缓存的方法 func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // 用barrier来防止缓存击穿,确保一个进程内只有一个请求去加载key对应的数据 val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // 从cache里读取数据 if err := c.doGetCache(key, v); err != nil { // 如果是预先放进来的placeholder(用来防止缓存穿透)的,那么就返回预设的errNotFound // 如果是未知错误,那么就直接返回,因为我们不能放弃缓存出错而直接把所有请求去请求DB, // 这样在高并发的场景下会把DB打挂掉的 if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // 请求DB // 如果返回的error是errNotFound,那么我们就需要在缓存里设置placeholder,防止缓存穿透 if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // 统计DB失败 c.stat.IncrementDbFails() return nil, err } // 把数据写入缓存 if err = cacheVal(v); err != nil { logx.Error(err) } } // 返回json序列化的数据 return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // 把数据写入到传入的v对象里 return jsonx.Unmarshal(val.([]byte), v) } 2. 基于唯一索引的缓存逻辑 因为这块比较复杂,所以我用不同颜色标识出来了响应的代码块和逻辑,block 2 其实跟基于主键的缓存是一样的,这里主要讲 block 1 的逻辑。 代码块的 block 1 部分分为两种情况: 通过索引能够从缓存里找到主键,此时就直接用主键走 block 2 的逻辑了,后续同上面基于主键的缓存逻辑 通过索引无法从缓存里找到主键 通过索引从DB里查询完整行记录,如有 error,返回 查到完整行记录后,会把主键到完整行记录的缓存和索引到主键的缓存同时写到 redis 里 返回所需的行记录数据 // v - 需要读取的数据对象 // key - 通过索引生成的缓存key // keyer - 用主键生成基于主键缓存的key的方法 // indexQuery - 用索引从DB读取完整数据的方法,需要返回主键 // primaryQuery - 用主键从DB获取完整数据的方法 func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // 先通过索引查询缓存,看是否有索引到主键的缓存 if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // 如果没有索引到主键的缓存,那么就通过索引查询完整数据 primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // 通过索引查询到了完整数据,设置found,后面直接使用,不需要再从缓存读取数据了 found = true // 将主键到完整数据的映射保存到缓存里,TakeWithExpire方法已经将索引到主键的映射保存到缓存了 return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // 已经通过索引找到了数据,直接返回即可 if found { return nil } // 通过主键从缓存读取数据,如果缓存没有,通过primaryQuery方法从DB读取并回写缓存再返回数据 return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } 我们来看一个实际的例子 func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // 生成基于索引的key indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // 基于主键生成完整数据缓存的key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // 基于索引的DB查询方法 func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // 基于主键的DB查询方法 func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // 错误处理,需要判断是否返回的是sqlc.ErrNotFound,如果是,我们用本package定义的ErrNotFound返回 // 避免使用者感知到有没有使用缓存,同时也是对底层依赖的隔离 switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } 所有上面这些缓存的自动管理代码都是可以通过 goctl 自动生成的,我们团队内部 CRUD 和缓存基本都是通过 goctl 自动生成的,可以节省大量开发时间,并且缓存代码本身也是非常容易出错的,即使有很好的代码经验,也很难每次完全写对,所以我们推荐尽可能使用自动的缓存代码生成工具去避免错误。 猜你想看 Go开源说第四期-go-zero缓存如何设计 Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"go-zero缓存设计之业务层缓存","keywords":"","body":"go-zero缓存设计之业务层缓存 在上一篇go-zero缓存设计之持久层缓存介绍了db层缓存,回顾一下,db层缓存主要设计可以总结为: 缓存只删除不更新 行记录始终只存储一份,即主键对应行记录 唯一索引仅缓存主键值,不直接缓存行记录(参考mysql索引思想) 防缓存穿透设计,默认一分钟 不缓存多行记录 前言 在大型业务系统中,通过对持久层添加缓存,对于大多数单行记录查询,相信缓存能够帮持久层减轻很大的访问压力,但在实际业务中,数据读取不仅仅只是单行记录, 面对大量多行记录的查询,这对持久层也会造成不小的访问压力,除此之外,像秒杀系统、选课系统这种高并发的场景,单纯靠持久层的缓存是不现实的,本节我们来 介绍go-zero实践中的缓存设计——biz缓存。 适用场景举例 选课系统 内容社交系统 秒杀 ... 像这些系统,我们可以在业务层再增加一层缓存来存储系统中的关键信息,如选课系统中学生选课信息,课程剩余名额;内容社交系统中某一段时间之间的内容信息等。 接下来,我们一内容社交系统来进行举例说明。 在内容社交系统中,我们一般是先查询一批内容列表,然后点击某条内容查看详情, 在没有添加biz缓存前,内容信息的查询流程图应该为: 从图以及上一篇文章go-zero缓存设计之持久层缓存中我们可以知道,内容列表的获取是没办法依赖缓存的, 如果我们在业务层添加一层缓存用来存储列表中的关键信息(甚至完整信息),那么多行记录的访问不在是一个问题,这就是biz redis要做的事情。 接下来我们来看一下设计方案,假设内容系统中单行记录包含以下字段 字段名称 字段类型 备注 id string 内容id title string 标题 content string 详细内容 createTime time.Time 创建时间 我们的目标是获取一批内容列表,而尽量避免内容列表走db造成访问压力,首先我们采用redis的sort set数据结构来存储,根需要存储的字段信息量,有两种redis存储方案: 缓存局部信息 对其关键字段信息(如:id等)按照一定规则压缩,并存储,score我们用createTime毫秒值(时间值相等这里不讨论),这种存储方案的好处是节约redis存储空间, 那另一方面,缺点就是需要对列表详细内容进行二次回查(但这次回查是会利用到持久层的行记录缓存的) 缓存完整信息 对发布的所有内容按照一定规则压缩后均进行存储,同样score我们还是用createTime毫秒值,这种存储方案的好处是业务的增、删、查、改均走redis,而db层这时候 就可以不用考虑行记录缓存了,持久层仅提供数据备份和恢复使用,从另一方面来看,其缺点也很明显,需要的存储空间、配置要求更高,费用也会随之增大。 示例代码: type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent 提供内容存储 func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent 提供内容删除 func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // 内容压缩 func compress(c *Content) string { // todo: do it yourself var ret string return ret } // 内容解压 func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime提供根据时间段进行数据查询 func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } 在以上例子中,redis是没有设置过期时间的,我们将增、删、改、查操作均同步到redis,我们认为内容社交系统的列表访问请求是比较高的情况下才做这样的方案设计, 除此之外,还有一些数据访问,没有想内容设计系统这么频繁的访问, 可能是某一时间段内访问量突如其来的增加,之后可能很长一段时间才会再访问一次,以此间隔, 或者说不会再访问了,面对这种场景,如果我又该如何考虑缓存的设计呢?在go-zero内容实践中,有两种方案可以解决这种问题: 增加内存缓存:通过内存缓存来存储当前可能突发访问量比较大的数据,常用的存储方案采用map数据结构来存储,map数据存储实现比较简单,但缓存过期处理则需要增加 定时器来出来,另一宗方案是通过go-zero库中的 Cache ,其是专门 用于内存管理. 采用biz redis,并设置合理的过期时间 总结 以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务 监控来衡量时候需要引入biz。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-queue.html":{"url":"go-queue.html","title":"go-zero分布式定时任务","keywords":"","body":" go-zero 分布式定时任务 日常任务开发中,我们会有很多异步、批量、定时、延迟任务要处理,go-zero中有go-queue,推荐使用go-queue去处理,go-queue本身也是基于go-zero开发的,其本身是有两种模式 dq : 依赖于beanstalkd,分布式,可存储,延迟、定时设置,关机重启可以重新执行,消息不会丢失,使用非常简单,go-queue中使用了redis setnx保证了每条消息只被消费一次,使用场景主要是用来做日常任务使用 kq:依赖于kafka,这个就不多介绍啦,大名鼎鼎的kafka,使用场景主要是做消息队列 我们主要说一下dq,kq使用也一样的,只是依赖底层不同,如果没使用过beanstalkd,没接触过beanstalkd的可以先google一下,使用起来还是挺容易的。 etc/job.yaml : 配置文件 Name: job Log: ServiceName: job Level: info #dq依赖Beanstalks、redis ,Beanstalks配置、redis配置 DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go :解析dq对应etc/*.yaml配置 /** * @Description 配置文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : 负责注册多任务 /** * @Description 注册job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) } ProducerLogic: 其中一个job业务逻辑 /** * @Description 生产者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i 另外一个Job业务逻辑 /** * @Description 消费者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description 配置 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go启动文件 /** * @Description 启动文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() //配置 var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) //注册job group := service.NewServiceGroup() handler.RegisterJob(ctx,group) //捕捉信号 ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { for { s := 常见问题: 为什么使用dq,需要使用redis? 因为beanstalk是单点服务,无法保证高可用。dq可以使用多个单点beanstalk服务,互相备份 & 保证高可用。使用redis解决重复消费问题。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"datacenter.html":{"url":"datacenter.html","title":"我是如何用go-zero 实现一个中台系统","keywords":"","body":"我是如何用 go-zero 实现一个中台系统 作者:Jack Luo 原文连接:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] 最近发现golang社区里出了一个新星的微服务框架,来自好未来,光看这个名字,就很有奔头,之前,也只是玩过go-micro,其实真正的还没有在项目中运用过,只是觉得 微服务,grpc 这些很高大尚,还没有在项目中,真正的玩过,我看了一下官方提供的工具真的很好用,只需要定义好,舒适文件jia结构 都生成了,只需要关心业务,加上最近 有个投票的活动,加上最近这几年中台也比较火,所以决定玩一下, 开源地址: https://github.com/jackluo2012/datacenter 先聊聊中台架构思路吧: 中台的概念大概就是把一个一个的app 统一起来,反正我是这样理解的。 先聊用户服务吧,现在一个公司有很多的公众号、小程序、微信的、支付宝的,还有 xxx xxx,很多的平台,每次开发的时候,我们总是需要做用户登陆的服务,不停的复制代码,然后我们就在思考能不能有一套独立的用户服务,只需要告诉我你需要传个你要登陆的平台(比如微信),微信登陆,需要的是客户端返回给服务端一个code ,然后服务端拿着这个code去微信获取用户信息,反正大家都明白。 我们决定,将所有的信息弄到配置公共服务中去,里面再存微信、支付宝以及其它平台的appid、appkey、还有支付的appid、appkey,这样就写一套。 最后说说实现吧,整个就一个repo: 网关,我们用的是: go-zero的Api服务 其它它的是服务,我们就是用的go-zero的rpc服务 看下目录结构 整个项目完成,我一个人操刀,写了1个来星期,我就实现了上面的中台系统。 datacenter-api服务 先看官方文档 https://go-zero.dev/cn/ 我们先把网关搭建起来: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter 查看book目录: ➜ datacenter tree . └── go.mod 0 directories, 1 file 创建api文件 ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api ├── user.api #用户 ├── votes.api #投票 ├── search.api #搜索 ├── questions.api #问答 └── go.mod 定义api服务 分别包含了上面的 公共服务,用户服务,投票活动服务 datacenter.api的内容: info( title: \"中台系统\"// TODO: add title desc: \"中台系统\"// TODO: add description author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) import \"user.api\" import \"votes.api\" import \"search.api\" import \"questions.api\" //获取 应用信息 type Beid { Beid int64 `json:\"beid\"` } type Token { Token string `json:\"token\"` } type WxTicket { Ticket string `json:\"ticket\"` } type Application { Sname string `json:\"Sname\"` //名称 Logo string `json:\"logo\"` // login Isclose int64 `json:\"isclose\"` //是否关闭 Fullwebsite string `json:\"fullwebsite\"` // 全站名称 } type SnsReq { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 BackUrl string `json:\"back_url\"` //登陆返回的地址 } type SnsResp { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 Appid string `json:\"appid\"` //sns 平台的id Title string `json:\"title\"` //名称 LoginUrl string `json:\"login_url\"` //微信登陆的地址 } type WxShareResp { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"获取站点的信息\" ) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"获取站点的社交属性信息\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) //获取分享的 @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } //上传需要登陆 @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"七牛上传凭证\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } user.api内容 //注册请求 type RegisterReq struct { // TODO: add members here and delete this comment Mobile string `json:\"mobile\"` //基本一个手机号码就完事 Password string `json:\"password\"` Smscode string `json:\"smscode\"` //短信码 } //登陆请求 type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` //1.密码登陆,2.短信登陆 Password string `json:\"password\"` } //微信登陆 type WxLoginReq struct { Beid int64 `json:\"beid\"` //应用id Code string `json:\"code\"` //微信登陆密钥 Ptyid int64 `json:\"ptyid\"` //对应平台 } //返回用户信息 type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } //返回APPUser type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service datacenter-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service datacenter-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } votes.api 投票内容 // 投票活动api type Actid struct { Actid int64 `json:\"actid\"` //活动id } type VoteReq struct { Aeid int64 `json:\"aeid\"` // 作品id Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` //投票票数 Viewcount int64 `json:\"viewcount\"` //浏览数 } // 活动返回的参数 type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` //活动名称 Descr string `json:\"descr\"` //活动描述 StartDate int64 `json:\"start_date\"` //活动时间 EnrollDate int64 `json:\"enroll_date\"` //投票时间 EndDate int64 `json:\"end_date\"` //活动结束时间 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 Type int64 `json:\"type\"` //投票方式 Num int64 `json:\"num\"` //投票几票 } //报名 type EnrollReq struct { Actid Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 } // 作品返回 type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` // 作品id Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 } @server( group: votes ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"活动访问+1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"获取报名的投票作品信息\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"获取报名的投票作品列表\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service datacenter-api { @doc( summary: \"投票\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } questions.api 问答内容: // 问答 抽奖 开始 @server( group: questions ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activitiesInfo get /questions/activities/info (Actid) returns (ActivityResp) @doc( summary: \"获取奖品信息\" ) @handler awardInfo get /questions/award/info (Actid) returns (ActivityResp) @handler awardList get /questions/award/list (Actid) returns (ActivityResp) } type AnswerReq struct { ActivityId int64 `json:\"actid\"` Answers string `json:\"answers\"` Score string `json:\"score\"` } type QuestionsAwardReq struct { ActivityId int64 `json:\"actid\"` AnswerId int64 `json:\"answerid\"` } type AnswerResp struct { Answers string `json:\"answers\"` Score string `json:\"score\"` } type AwardConvertReq struct { UserName string `json:\"username\"` Phone string `json:\"phone\"` LotteryId int64 `json:\"lotteryid\"` } @server( jwt: Auth group: questions middleware: Usercheck ) service datacenter-api { @doc( summary: \"获取题目\" ) @handler lists get /questions/lists (VoteReq) returns (AnswerResp) @doc( summary: \"提交答案\" ) @handler change post /questions/change (AnswerReq) returns (VoteResp) @doc( summary: \"获取分数\" ) @handler grade get /questions/grade (VoteReq) returns (VoteResp) @doc( summary: \"开始转盘\" ) @handler turntable post /questions/lottery/turntable (EnrollReq) returns (EnrollResp) @doc( summary: \"填写中奖信息人\" ) @handler lottery post /questions/lottery/convert (AwardConvertReq) returns (EnrollResp) } // 问答 抽奖 结束 search.api 搜索 type SearchReq struct { Keyword string `json:\"keyword\"` Page string `json:\"page\"` Size string `json:\"size\"` } type SearchResp struct { Data []ArticleReq `json:\"data\"` } type ArticleReq struct{ NewsId string `json:\"NewsId\"` NewsTitle string `json:\"NewsTitle\"` ImageUrl string `json:\"ImageUrl\"` } @server( group: search middleware: Admincheck ) service datacenter-api { @doc( summary: \"搜索\" ) @handler article get /search/article (SearchReq) returns (SearchResp) @handler articleInit get /search/articel/init (SearchReq) returns (SearchResp) @handler articleStore post /search/articel/store (ArticleReq) returns (ArticleReq) } 上面基本上写就写的API及文档的思路 生成datacenter api服务 ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter treer . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files 我们打开 etc/datacenter-api.yaml 把必要的配置信息加上 Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: 你的jwtwon Secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: 密码 Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc 上面的 UserRpc, CommonRpc ,还有 VotesRpc 这些我先写上,后面再来慢慢加。 我们先来写 CommonRpc 服务。 CommonRpc服务 新建项目目录 ➜ datacenter mkdir -p common/rpc && cd common/rpc 直接就新建在了,datacenter目录中,因为common 里面,可能以后会不只会提供rpc服务,可能还有api的服务,所以又加了rpc目录 goctl创建模板 ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto 往里面填入内容: ➜ rpc cat common.proto syntax = \"proto3\"; option go_package = \"common\"; package common; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } // 请求的api message AppConfigReq { int64 beid=1; int64 ptyid=2; } // 返回的值 message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl生成rpc服务 ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files 基本上,就把所有的目录规范和结构的东西都生成了,就不用纠结项目目录了,怎么放了,怎么组织了。 看一下,配置信息,里面可以写入mysql和其它redis的信息: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc 我们再来加上数据库服务: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files 这样基本的一个 rpc 就写完了,然后我们将rpc 和model 还有api串连起来,这个官方的文档已经很详细了,这里就只是贴一下代码: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } 再在svc中修改: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } 上面的代码已经将 rpc 和 model 数据库关联起来了,我们现在再将 rpc 和 api 关联起来: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } 加入 svc 服务中: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"调用 %s 方法 耗时: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } 这样基本上,我们就可以在 logic 的文件目录中调用了: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { //检查 缓存中是否有值 err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } 这样,基本就连接起来了,其它基本上就不用改了,UserRPC, VotesRPC 类似,这里就不在写了。 使用心得 go-zero 的确香,因为它有一个 goctl 的工具,他可以自动的把代码结构全部的生成好,我们就不再去纠结,目录结构 ,怎么组织,没有个好几年的架构能力是不好实现的,有什么规范那些,并发,熔断,完全不用,考虑其它的,专心的实现业务就好,像微服务,还要有服务发现,一系列的东西,都不用关心,因为 go-zero 内部已经实现了。 我写代码也写了有10多年了,之前一直用的 php,比较出名的就 laravel,thinkphp,基本上就是模块化的,像微服务那些实现真的有成本,但是你用上go-zero,你就像调api接口一样简单的开发,其它什么服务发现,那些根本就不用关注了,只需要关注业务。 一个好的语言,框架,他们的底层思维,永远都是效率高,不加班的思想,我相信go-zero会提高你和你团队或是公司的效率。go-zero的作者说,他们有个团队专门整理go-zero框架,目的也应该很明显,那就是提高,他们自己的开发效率,流程化,标准化,是提高工作效率的准则,像我们平时遇到了问题,或是遇到了bug,我第一个想到的不是怎么去解决我的bug,而是在想我的流程是不是有问题,我的哪个流程会导致bug,最后我相信 go-zero 能成为 微服务开发 的首选框架。 最后说说遇到的坑吧: grpc grpc 本人第一次用,然后就遇到了,有些字符为空时,字段值不显示的问题: 通过 grpc 官方库中的 jsonpb 来实现,官方在它的设定中有一个结构体用来实现 protoc buffer 转换为JSON结构,并可以根据字段来配置转换的要求。 跨域问题 go-zero 中设置了,感觉没有效果,大佬说通过nginx 设置,后面发现还是不行,最近强行弄到了一个域名下,后面有时间再解决。 sqlx go-zero 的 sqlx 问题,这个真的费了很长的时间: time.Time 这个数据结构,数据库中用的是 timestamp 这个 比如我的字段 是delete_at 默认数库设置的是null ,结果插入的时候,就报了 Incorrect datetime value: '0000-00-00' for column 'deleted_at' at row 1\"} 这个错,查询的时候报 deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" 后面果断去掉了这个字段,字段上面加上 .omitempty 这个标签,好像也有用,db:\".omitempty\" 其次就是这个 Conversion from collation utf8_general_ci into utf8mb4_unicode_ci,这个导致的大概原因是,现在都喜欢用emj表情了,mysql数据识别不了。 数据连接 mysql 这边照样按照原始的方式,将配置文件修改编码格式,重新创建数据库,并且设置数据库编码为utf8mb4,排序规则为 utf8mb4_unicode_ci。 这样的话,所有的表还有string字段都是这个编码格式,如果不想所有的都是,可以单独设置,这个不是重点.因为在navicat上都好设置,手动点一下就行了。 重点来了:golang中使用的是 github.com/go-sql-driver/mysql 驱动,将连接 mysql的 dsn(因为我这使用的是gorm,所以dsn可能跟原生的格式不太一样,不过没关系, 只需要关注 charset 和 collation 就行了) root:password@/name?parseTime=True&loc=Local&charset=utf8 修改为: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"stream.html":{"url":"stream.html","title":"流数据处理利器","keywords":"","body":"流数据处理利器 流处理 (Stream processing) 是一种计算机编程范式,其允许给定一个数据序列 (流处理数据源),一系列数据操作 (函数) 被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到 Kafka,然后再由 Job 消费 Kafaka 写到 elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx go-zero 是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx ,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce 工具进行处理。 总结 本篇文章介绍了流处理的基本概念和go-zero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"online-exchange.html":{"url":"online-exchange.html","title":"10月3日线上交流问题汇总","keywords":"","body":"10月3日线上交流问题汇总 go-zero适用场景 希望说说应用场景,各个场景下的优势 高并发的微服务系统 支撑千万级日活,百万级QPS 完整的微服务治理能力 支持自定义中间件 很好的管理了数据库和缓存 有效隔离故障 低并发的单体系统 这种系统直接使用api层即可,无需rpc服务 各个功能的使用场景以及使用案例 限流 熔断 降载 超时 可观测性 go-zero的实际体验 服务很稳 前后端接口一致性,一个api文件即可生成前后端代码 规范、代码量少,意味着bug少 免除api文档,极大降低沟通成本 代码结构完全一致,便于维护和接手 微服务的项目结构, monorepo的 CICD 处理 bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model mono repo的CI我们是通过gitlab做的,CD使用jenkins CI尽可能更严格的模式,比如-race,使用sonar等工具 CD有开发、测试、预发、灰度和正式集群 晚6点上灰度、无故障的话第二天10点自动同步到正式集群 正式集群分为多个k8s集群,有效的防止单集群故障,直接摘除即可,集群升级更有好 如何部署,如何监控? 全量K8S,通过jenkins自动打包成docker镜像,按照时间打包tag,这样可以一眼看出哪一天的镜像 上面已经讲了,预发->灰度->正式 Prometheus+自建dashboard服务 基于日志检测服务和请求异常 如果打算换go-zero框架重构业务,如何做好线上业务稳定安全用户无感切换?另外咨询下如何进行服务划分? 逐步替换,从外到内,加个proxy来校对,校对一周后可以切换 如有数据库重构,则需要做好新老同步 服务划分按照业务来,遵循从粗到细的原则,避免一个api一个微服务 数据拆分对于微服务来讲尤为重要,上层好拆,数据难拆,尽可能保证按照业务拆分数据 服务发现 服务发现 etcd 的 key 的设计 服务key+时间戳,服务进程数存在时间戳冲突的概率极低,忽略 etcd服务发现与治理, 异常捕获与处理异常 为啥k8s还使用etcd做服务发现,因为dns的刷新有延迟,导致滚动更新会有大量失败,而etcd可以做到完全无损更新 etcd集群直接部署在k8s集群内,因为多个正式集群,集群单点和注册避免混乱 针对etcd异常或者leader切换,自动侦测并刷新,当etcd有异常不能恢复时,不会刷新服务列表,保障服务依然可用 缓存的设计与使用案例 分布式多redis集群,线上最大几十个集群为同一个服务提供缓存服务 无缝扩缩容 不存在没有过期时间的缓存,避免大量不常使用的数据占用资源,默认一周 缓存穿透,没有的数据会短暂缓存一分钟,避免刷接口或大量不存在的数据请求带垮系统 缓存击穿,一个进程只会刷新一次同一个数据,避免热点数据被大量同时加载 缓存雪崩,对缓存过期时间自动做了jitter,5%的标准变差,使得一周的过期时间分布在16小时内,有效防止了雪崩 我们线上数据库都有缓存,否则无法支撑海量并发 自动缓存管理已经内置于go-zero,并可以通过goctl自动生成代码 能否讲解下, 中间件,拦截器的设计思想 洋葱模型 本中间件处理,比如限流,熔断等,然后决定是否调用next next调用 对next调用返回结果做处理 微服务的事务处理怎么实现好,gozero分布式事务设计和实现,有什么好中间件推荐 2PC,两阶段提交 TCC,Try-Confirm-Cancel 消息队列,最大尝试 人工补偿 多级 goroutine 的异常捕获 ,怎么设计比较好 微服务系统请求异常应该隔离,不能让单个异常请求带崩整个进程 go-zero自带了RunSafe/GoSafe,用来防止单个异常请求导致进程崩溃 监控需要跟上,防止异常过量而不自知 fail fast和故障隔离的矛盾点 k8s配置的生成与使用(gateway, service, slb) 内部自动生成k8s的yaml文件,过于依赖配置而未开源 打算在bookstore的示例里加上k8s配置样板 slb->nginx->nodeport->api gateway->rpc service gateway限流、熔断和降载 限流分为两种:并发控制和分布式限流 并发控制用来防止瞬间过量请求,保护系统不被打垮 分布式限流用来给不同服务配置不同的quota 熔断是为了对依赖的服务进行保护,当一个服务出现大量异常的时候,调用者应该给予保护,使其有机会恢复正常,同时也达到fail fast的效果 降载是为了保护当前进程资源耗尽而陷入彻底不可用,确保尽可能服务好能承载的最大请求量 降载配合k8s,可以有效保护k8s扩容,k8s扩容分钟级,go-zero降载秒级 介绍core中好用的组件,如timingwheel等,讲讲设计思路 布隆过滤器 进程内cache RollingWindow TimingWheel 各种executors fx包,map/reduce/filter/sort/group/distinct/head/tail... 一致性hash实现 分布式限流实现 mapreduce,带cancel能力 syncx包里有大量的并发工具 如何快速增加一种rpc协议支持,將跨机发现改为调本机节点,并关闭复杂filter和负载均衡功能 go-zero跟grpc关系还是比较紧密的,设计之初没有考虑支持grpc以外的协议 如果要增加的话,那就只能fork出来魔改了 调本机直接用direct的scheme即可 为啥要去掉filter和负载均衡?如果要去的话,fork了改,但没必要 日志和监控和链路追踪的设计和实现思路,最好有大概图解 日志和监控我们使用prometheus, 自定义dashboard服务,捆绑提交数据(每分钟) 链路追踪可以看出调用关系,自动记录trace日志 go-zero框架有用到什么池化技术吗?如果有,在哪些core代码里面可以参考 一般不需要提前优化,过度优化是大忌 core/syncx/pool.go里面定义了带过期时间的通用池化技术 go-zero用到了那些性能测试方法框架,有代码参考吗?可以说说思路和经验 go benchmark 压测可以通过现有业务日志样本,来按照预估等比放大 压测一定要压到系统扛不住,看第一个瓶颈在哪里,改完再压,循环 说一下代码的抽象经验和心得 Don’t repeat yourself 你未必需要它,之前经常有业务开发人员问我可不可以增加这个功能或那个功能,我一般都会仔细询问深层次目的,很多时候会发现其实这个功能是多余的,不需要才是最佳实践 Martin Fowler提出出现三次再抽象的原则,有时有些同事会找我往框架里增加一个功能,我思考后经常会回答这个你先在业务层写,其它地方也有需要了你再告诉我,三次出现我会考虑集成到框架里 一个文件应该尽量只做一件事,每个文件尽可能控制在200行以内,一个函数尽可能控制在50行以内,这样不需要滚动就可以看到整个函数 需要抽象和提炼的能力,多去思考,经常回头思考之前的架构或实现 你会就go-zero 框架从设计到实践出书吗?框架以后的发展规划是什么? 暂无出书计划,做好框架是最重要的 继续着眼于工程效率 提升服务治理能力 帮助业务开发尽可能快速落地 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"contributor.html":{"url":"contributor.html","title":"贡献人员","keywords":"","body":"社区贡献 作者 kevwan go-zero参与人员 kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy heyanfu sjatsh Mikaelemmmm foliet taobig Suyghur wubenqi shenbaise9527 fondoger reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl weicut zzzfwww HarryWang29 soasurs workman-Lu wuqinqiang ShyunnY appleboy Howie59 pipi-lv AlexLast almas1992 SnakeHacker sohamtembhurne toby1991 kscooo bensonfx cuishuang fyyang wangzeping722 guonaihong heyehang masonchen2014 lchjczw lucaq ahmczsy moyrne mongobaba mlboy me-cs yangwenmai magickeha lowang-bh lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang zeromake 2822132073 linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 lovelly zzhaolei zzZZzzz888 sixwaaaay liuqing6767 lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle peasfarmer qwxingzhe SeigeC jsonMark RivenChan toventang vankillua shssen rcyw weibobo windk wojiukankan wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 yiGmMk liyiwu l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen cuisongliu dahaihu dylanNew edieruby elza2 fffreedom codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog 文档贡献人员 kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:18 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"文档贡献","keywords":"","body":"文档贡献 怎么贡献文档? 点击顶部\"编辑此页\"按钮即可进入源码仓库对应的文件,开发人员将修改(添加)的文档通过pr形式提交, 我们收到pr后会进行文档审核,一旦审核通过即可更新文档。 可以贡献哪些文档? 文档编写错误 文档不规范、不完整 go-zero应用实践、心得 组件中心 文档pr通过后文档多久会更新? 在pr接受后,github action会自动build gitbook并发布,因此在github action成功后1-2分钟即可查看更新后的文档。 文档贡献注意事项 纠错、完善源文档可以直接编写原来的md文件 新增组件文档需要保证文档排版、易读,且组件文档需要放在组件中心子目录中 go-zero应用实践分享可以直接放在开发实践子目录下 目录结构规范 目录结构不宜过深,最好不要超过3层 组件文档需要在归属到组件中心,如* [开发实践](practise.md) * [logx](logx.md) * [bloom](bloom.md) * [executors](executors.md) * 你的文档目录名称 应用实践需要归属到开发实践,如* [开发实践](practise.md) * [我是如何用go-zero 实现一个中台系统](datacenter.md) * [流数据处理利器](stream.md) * [10月3日线上交流问题汇总](online-exchange.md * 你的文档目录名称 开发实践文档模板 # 标题 > 作者:填入作者名称 > > 原文连接: 原文连接 some markdown content 猜你想看 怎么参与贡献 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error.html":{"url":"error.html","title":"常见错误处理","keywords":"","body":"常见错误处理 Windows上报错 A required privilege is not held by the client. 解决方法:\"以管理员身份运行\" goctl 即可。 grpc引起错误 错误一 protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. 解决方法: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go安装失败 go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout 请确认GOPROXY已经设置,GOPROXY设置见go module配置 api服务启动失败 error: config file etc/user-api.yaml, error: type mismatch for field xx 请确认user-api.yaml配置文件中配置项是否已经配置,如果有值,检查一下yaml配置文件是否符合yaml格式。 goctl找不到 command not found: goctl 请确保goctl已经安装或者goctl是否已经添加到环境变量 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"source.html":{"url":"source.html","title":"相关源码","keywords":"","body":"相关源码 demo源码 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tips.html":{"url":"tips.html","title":"阅读须知","keywords":"","body":"阅读须知 本文档从快速入门,详细项目开发流程,go-zero服务设计思想,goctl工具的使用等维度进行了介绍, 对于刚刚接触go或go-zero的同学需要把这些篇幅都看完才能有所了解,因此有些费力,这里建议大家阅读的方法。 保持耐心跟着文档目录进行,文档是按照从简单到深入的渐进式过程编写的。 在遇到问题或错误时,请一定记住多查FAQ。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"shorturl.html":{"url":"shorturl.html","title":"快速构建高并发微服务","keywords":"","body":"快速构建高并发微服务 English | 简体中文 0. 为什么说做好微服务很难 要想做好微服务,我们需要理解和掌握的知识点非常多,从几个维度上来说: 基本功能层面 并发控制 & 限流,避免服务被突发流量击垮 服务注册与服务发现,确保能够动态侦测增减的节点 负载均衡,需要根据节点承受能力分发流量 超时控制,避免对已超时请求做无用功 熔断设计,快速失败,保障故障节点的恢复能力 高阶功能层面 请求认证,确保每个用户只能访问自己的数据 链路追踪,用于理解整个系统和快速定位特定请求的问题 日志,用于数据收集和问题定位 可观测性,没有度量就没有优化 对于其中每一点,我们都需要用很长的篇幅来讲述其原理和实现,那么对我们后端开发者来说,要想把这些知识点都掌握并落实到业务系统里,难度是非常大的,不过我们可以依赖已经被大流量验证过的框架体系。go-zero 微服务框架就是为此而生。 另外,我们始终秉承 工具大于约定和文档 的理念。我们希望尽可能减少开发人员的心智负担,把精力都投入到产生业务价值的代码上,减少重复代码的编写,所以我们开发了 goctl 工具。 下面我通过短链微服务来演示通过 go-zero 快速的创建微服务的流程,走完一遍,你就会发现:原来编写微服务如此简单! 1. 什么是短链服务 短链服务就是将长的 URL 网址,通过程序计算等方式,转换为简短的网址字符串。 写此短链服务是为了从整体上演示 go-zero 构建完整微服务的过程,算法和实现细节尽可能简化了,所以这不是一个高阶的短链服务。 2. 短链微服务架构图 这里只用了 Transform RPC 一个微服务,并不是说 API Gateway 只能调用一个微服务,只是为了最简演示 API Gateway 如何调用 RPC 微服务而已 在真正项目里要尽可能每个微服务使用自己的数据库,数据边界要清晰 3. goctl 各层代码生成一览 所有绿色背景的功能模块是自动生成的,按需激活,红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑,各层示意图分别如下: API Gateway RPC model 下面我们来一起完整走一遍快速构建微服务的流程,Let’s Go!🏃‍♂️ 4. 准备工作 安装 etcd, mysql, redis 安装 protoc-gen-go $ go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 安装 protoc $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip $ unzip protoc-3.14.0-linux-x86_64.zip $ mv bin/protoc /usr/local/bin/ 安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 创建工作目录 shorturl 和 shorturl/api mkdir -p shorturl/api 在 shorturl 目录下执行 go mod init shorturl 初始化 go.mod module shorturl go 1.15 require ( github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.4.2 github.com/zeromicro/go-zero v1.3.0 golang.org/x/net v0.0.0-20200707034311-ab3426394381 google.golang.org/grpc v1.29.1 ) 注意:这里可能存在 grpc 版本依赖的问题,可以用以上配置 5. 编写 API Gateway 代码 在 shorturl/api 目录下通过 goctl 生成 api/shorturl.api: $ goctl api -o shorturl.api 编辑 api/shorturl.api,为了简洁,去除了文件开头的 info,代码如下: type ( expandReq { shorten string `form:\"shorten\"` } expandResp { url string `json:\"url\"` } ) type ( shortenReq { url string `form:\"url\"` } shortenResp { shorten string `json:\"shorten\"` } ) service shorturl-api { @server( handler: ShortenHandler ) get /shorten(shortenReq) returns(shortenResp) @server( handler: ExpandHandler ) get /expand(expandReq) returns(expandResp) } type 用法和 go 一致,service 用来定义 get/post/head/delete 等 api 请求,解释如下: service shorturl-api { 这一行定义了 service 名字 @server 部分用来定义 server 端用到的属性 handler 定义了服务端 handler 名字 get /shorten(shortenReq) returns(shortenResp) 定义了 get 方法的路由、请求参数、返回参数等 使用 goctl 生成 API Gateway 代码 $ goctl api go -api shorturl.api -dir . 生成的文件结构如下: . ├── api │ ├── etc │ │ └── shorturl-api.yaml // 配置文件 │ ├── internal │ │ ├── config │ │ │ └── config.go // 定义配置 │ │ ├── handler │ │ │ ├── expandhandler.go // 实现 expandHandler │ │ │ ├── routes.go // 定义路由处理 │ │ │ └── shortenhandler.go // 实现 shortenHandler │ │ ├── logic │ │ │ ├── expandlogic.go // 实现 ExpandLogic │ │ │ └── shortenlogic.go // 实现 ShortenLogic │ │ ├── svc │ │ │ └── servicecontext.go // 定义 ServiceContext │ │ └── types │ │ └── types.go // 定义请求、返回结构体 │ ├── shorturl.api │ └── shorturl.go // main 入口定义 ├── go.mod └── go.sum 启动 API Gateway 服务,默认侦听在 8888 端口 $ go run shorturl.go -f etc/shorturl-api.yaml 测试 API Gateway 服务 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 27 Aug 2020 14:31:39 GMT Content-Length: 15 {\"shorten\":\"\"} 可以看到我们 API Gateway 其实啥也没干,就返回了个空值,接下来我们会在 rpc 服务里实现业务逻辑 可以修改 internal/svc/servicecontext.go 来传递服务依赖(如果需要) 实现逻辑可以修改 internal/logic 下的对应文件 可以通过 goctl 生成各种客户端语言的 api 调用代码 到这里,你已经可以通过 goctl 生成客户端代码给客户端同学并行开发了,支持多种语言,详见文档 6. 编写 transform rpc 服务 在 shorturl 目录下创建 rpc 目录 在 rpc/transform 目录下编写 transform.proto 文件 可以通过命令生成 proto 文件模板 $ goctl rpc template -o transform.proto 修改后文件内容如下: syntax = \"proto3\"; package transform; message expandReq { string shorten = 1; } message expandResp { string url = 1; } message shortenReq { string url = 1; } message shortenResp { string shorten = 1; } service transformer { rpc expand(expandReq) returns(expandResp); rpc shorten(shortenReq) returns(shortenResp); } 用 goctl 生成 rpc 代码,在 rpc/transform 目录下执行命令 $ goctl rpc proto -src transform.proto -dir . 注意:不能在 GOPATH 目录下执行以上命令 文件结构如下: rpc/transform ├── etc │ └── transform.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ ├── expandlogic.go // expand 业务逻辑在这里实现 │ │ └── shortenlogic.go // shorten 业务逻辑在这里实现 │ ├── server │ │ └── transformerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义 ServiceContext,传递依赖 ├── pb │ └── transform.pb.go ├── transform.go // rpc 服务 main 函数 ├── transform.proto └── transformer ├── transformer.go // 提供了外部调用方法,无需修改 ├── transformer_mock.go // mock 方法,测试用 └── types.go // request/response 结构体定义 直接可以运行,如下: $ go run transform.go -f etc/transform.yaml Starting rpc server at 127.0.0.1:8080... 查看服务是否注册 $ ETCDCTL_API=3 etcdctl get transform.rpc --prefix transform.rpc/7587851893787585061 127.0.0.1:8080 etc/transform.yaml 文件里可以修改侦听端口等配置 7. 修改 API Gateway 代码调用 transform rpc 服务 修改配置文件 shorturl-api.yaml,增加如下内容 Transform: Etcd: Hosts: - localhost:2379 Key: transform.rpc 通过 etcd 自动去发现可用的 transform 服务 修改 internal/config/config.go 如下,增加 transform 服务依赖 type Config struct { rest.RestConf Transform zrpc.RpcClientConf // 手动代码 } 修改 internal/svc/servicecontext.go,如下: type ServiceContext struct { Config config.Config Transformer transformer.Transformer // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Transformer: transformer.NewTransformer(zrpc.MustNewClient(c.Transform)), // 手动代码 } } 通过 ServiceContext 在不同业务逻辑之间传递依赖 修改 internal/logic/expandlogic.go 里的 Expand 方法,如下: func (l *ExpandLogic) Expand(req types.ExpandReq) (types.ExpandResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Expand(l.ctx, &transformer.ExpandReq{ Shorten: req.Shorten, }) if err != nil { return types.ExpandResp{}, err } return types.ExpandResp{ Url: resp.Url, }, nil // 手动代码结束 } 通过调用 transformer 的 Expand 方法实现短链恢复到 url 修改 internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(req types.ShortenReq) (types.ShortenResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Shorten(l.ctx, &transformer.ShortenReq{ Url: req.Url, }) if err != nil { return types.ShortenResp{}, err } return types.ShortenResp{ Shorten: resp.Shorten, }, nil // 手动代码结束 } 有的版本生成返回值可能是指针类型,需要自己调整下 通过调用 transformer 的 Shorten 方法实现 url 到短链的变换 至此,API Gateway 修改完成,虽然贴的代码多,但是其中修改的是很少的一部分,为了方便理解上下文,我贴了完整代码,接下来处理 CRUD+cache 8. 定义数据库表结构,并生成 CRUD+cache 代码 shorturl 下创建 rpc/transform/model 目录:mkdir -p rpc/transform/model 在 rpc/transform/model 目录下编写创建 shorturl 表的 sql 文件 shorturl.sql,如下: CREATE TABLE `shorturl` ( `shorten` varchar(255) NOT NULL COMMENT 'shorten key', `url` varchar(255) NOT NULL COMMENT 'original url', PRIMARY KEY(`shorten`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 创建 DB 和 table create database gozero; source shorturl.sql; 在 rpc/transform/model 目录下执行如下命令生成 CRUD+cache 代码,-c 表示使用 redis cache $ goctl model mysql ddl -c -src shorturl.sql -dir . 也可以用 datasource 命令代替 ddl 来指定数据库链接直接从 schema 生成 生成后的文件结构如下: Plain Text rpc/transform/model ├── shorturl.sql ├── shorturlmodel.go // CRUD+cache 代码 └── vars.go // 定义常量和变量 9. 修改 shorten/expand rpc 代码调用 crud+cache 代码 修改 rpc/transform/etc/transform.yaml,增加如下内容: DataSource: root:password@tcp(localhost:3306)/gozero Table: shorturl Cache: - Host: localhost:6379 可以使用多个 redis 作为 cache,支持 redis 单点或者 redis 集群 修改 rpc/transform/internal/config/config.go,如下: type Config struct { zrpc.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 } 增加了 mysql 和 redis cache 配置 修改 rpc/transform/internal/svc/servicecontext.go,如下: type ServiceContext struct { c config.Config Model model.ShorturlModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache), // 手动代码 } } 修改 rpc/transform/internal/logic/expandlogic.go,如下: func (l *ExpandLogic) Expand(in *transform.ExpandReq) (*transform.ExpandResp, error) { // 手动代码开始 res, err := l.svcCtx.Model.FindOne(l.ctx, in.Shorten) if err != nil { return nil, err } return &transform.ExpandResp{ Url: res.Url, }, nil // 手动代码结束 } 修改 rpc/transform/internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(in *transform.ShortenReq) (*transform.ShortenResp, error) { // 手动代码开始,生成短链接 key := hash.Md5Hex([]byte(in.Url))[:6] object, _ := l.svcCtx.Model.FindOne(l.ctx, key) if object != nil { return &transform.ShortenResp{ Shorten: key, }, nil } _, err := l.svcCtx.Model.Insert(l.ctx, &model.Shorturl{ Shorten: key, Url: in.Url, }) if err != nil { return nil, err } return &transform.ShortenResp{ Shorten: key, }, nil // 手动代码结束 } 至此代码修改完成,凡是手动修改的代码我加了标注 注意: undefined cache,你需要 import \"github.com/zeromicro/go-zero/core/stores/cache\" undefined model, sqlx, hash 等,你需要在文件中 import \"shorturl/rpc/transform/model\" import \"github.com/zeromicro/go-zero/core/stores/sqlx\" 10. 完整调用演示 shorten api 调用 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:49:49 GMT Content-Length: 21 {\"shorten\":\"f35b2a\"} expand api 调用 $ curl -i \"http://localhost:8888/expand?shorten=f35b2a\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:51:53 GMT Content-Length: 34 {\"url\":\"http://www.xiaoheiban.cn\"} 11. Benchmark 因为写入依赖于 mysql 的写入速度,就相当于压 mysql 了,所以压测只测试了 expand 接口,相当于从 mysql 里读取并利用缓存,shorten.lua 里随机从 db 里获取了 100 个热 key 来生成压测请求 可以看出在我的 MacBook Pro 上能达到 3 万 + 的 qps。 12. 完整代码 https://github.com/zeromicro/zero-examples/tree/main/shorturl 12. 总结 我们一直强调 工具大于约定和文档。 go-zero 不只是一个框架,更是一个建立在框架 + 工具基础上的,简化和规范了整个微服务构建的技术体系。 我们在保持简单的同时也尽可能把微服务治理的复杂度封装到了框架内部,极大的降低了开发人员的心智负担,使得业务开发得以快速推进。 通过 go-zero+goctl 生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。 有任何好的提升工程效率的想法,随时欢迎交流!👏 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"breaker-algorithms.html":{"url":"breaker-algorithms.html","title":"熔断原理与实现","keywords":"","body":"熔断原理与实现 在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机 由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断 熔断器原理 熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误 在这种模式下,服务调用方为每一个调用服务(调用路径)维护一个状态机,在这个状态机中有三个状态: 关闭(Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态 打开(Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复 半打开(Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到打开状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮 服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回 熔断器引入 上面介绍了熔断器的原理,在了解完原理后,你是否有思考我们如何引入熔断器呢?一种方案是在业务逻辑中可以加入熔断器,但显然是不够优雅也不够通用的,因此我们需要把熔断器集成在框架内,在zRPC框架内就内置了熔断器 我们知道,熔断器主要是用来保护调用端,调用端在发起请求的时候需要先经过熔断器,而客户端拦截器正好兼具了这个这个功能,所以在zRPC框架内熔断器是实现在客户端拦截器内,拦截器的原理如下图: 对应的代码为: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // 基于请求方法进行熔断 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正发起调用 return invoker(ctx, method, req, reply, cc, opts...) // codes.Acceptable判断哪种错误需要加入熔断错误计数 }, codes.Acceptable) } 熔断器实现 zRPC中熔断器的实现参考了Google Sre过载保护算法,该算法的原理如下: 请求数量(requests):调用方发起请求的数量总和 请求接受数量(accepts):被调用方正常处理的请求数量 在正常情况下,这两个值是相等的,随着被调用方服务出现异常开始拒绝请求,请求接受数量(accepts)的值开始逐渐小于请求数量(requests),这个时候调用方可以继续发送请求,直到requests = K * accepts,一旦超过这个限制,熔断器就回打开,新的请求会在本地以一定的概率被抛弃直接返回错误,概率的计算公式如下: 通过修改算法中的K(倍值),可以调节熔断器的敏感度,当降低该倍值会使自适应熔断算法更敏感,当增加该倍值会使得自适应熔断算法降低敏感度,举例来说,假设将调用方的请求上限从 requests = 2 acceptst 调整为 requests = 1.1 accepts 那么就意味着调用方每十个请求之中就有一个请求会触发熔断 代码路径为go-zero/core/breaker type googleBreaker struct { k float64 // 倍值 默认1.5 stat *collection.RollingWindow // 滑动时间窗口,用来对请求失败和成功计数 proba *mathx.Proba // 动态概率 } 自适应熔断算法实现 func (b *googleBreaker) accept() error { accepts, total := b.history() // 请求接受数量和请求总量 weightedAccepts := b.k * float64(accepts) // 计算丢弃请求概率 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio 每次发起请求会调用doReq方法,在这个方法中首先通过accept效验是否触发熔断,acceptable用来判断哪些error会计入失败计数,定义如下: func Acceptable(err error) bool { switch status.Code(err) { case codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss: // 异常请求错误 return false default: return true } } 如果请求正常则通过markSuccess把请求数量和请求接受数量都加一,如果请求不正常则只有请求数量会加一 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { // 判断是否触发熔断 if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 执行真正的调用 err := req() // 正常请求计数 if acceptable(err) { b.markSuccess() } else { // 异常请求计数 b.markFailure() } return err } 总结 调用端可以通过熔断机制进行自我保护,防止调用下游服务出现异常,或者耗时过长影响调用端的业务逻辑,很多功能完整的微服务框架都会内置熔断器。其实,不仅微服务调用之间需要熔断器,在调用依赖资源的时候,比如mysql、redis等也可以引入熔断器的机制。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"collection.html":{"url":"collection.html","title":"进程内缓存组件 collection.Cache","keywords":"","body":"通过 collection.Cache 进行缓存 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 进程内缓存工具collection.Cache 在做服务器开发的时候,相信都会遇到使用缓存的情况,go-zero 提供的简单的缓存封装 collection.Cache,简单使用方式如下 // 初始化 cache,其中 WithLimit 可以指定最大缓存的数量 c, err := collection.NewCache(time.Minute, collection.WithLimit(10000)) if err != nil { panic(err) } // 设置缓存 c.Set(\"key\", user) // 获取缓存,ok:是否存在 v, ok := c.Get(\"key\") // 删除缓存 c.Del(\"key\") // 获取缓存,如果 key 不存在的,则会调用 func 去生成缓存 v, err := c.Take(\"key\", func() (interface{}, error) { return user, nil }) cache 实现的建的功能包括 缓存自动失效,可以指定过期时间 缓存大小限制,可以指定缓存个数 缓存增删改 缓存命中率统计 并发安全 缓存击穿 实现原理: Cache 自动失效,是采用 TimingWheel(https://github.com/zeromicro/zeromicro/blob/master/core/collection/timingwheel.go) 进行管理的 timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) Cache 大小限制,是采用 LRU 淘汰策略,在新增缓存的时候会去检查是否已经超出过限制,具体代码在 keyLru 中实现 func (klru *keyLru) add(key string) { if elem, ok := klru.elements[key]; ok { klru.evicts.MoveToFront(elem) return } // Add new item elem := klru.evicts.PushFront(key) klru.elements[key] = elem // Verify size not exceeded if klru.evicts.Len() > klru.limit { klru.removeOldest() } } Cache 的命中率统计,是在代码中实现 cacheStat,在缓存命中丢失的时候自动统计,并且会定时打印使用的命中率, qps 等状态. 打印的具体效果如下 cache(proc) - qpm: 2, hit_ratio: 50.0%, elements: 0, hit: 1, miss: 1 缓存击穿包含是使用 syncx.SingleFlight(https://github.com/zeromicro/zeromicro/blob/master/core/syncx/singleflight.go) 进行实现的,就是将同时请求同一个 key 的请求, 关于 SingleFlight 后续会继续补充。 相关具体实现是在: func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { v, e := fetch() if e != nil { return nil, e } c.Set(key, v) return v, nil }) if err != nil { return nil, err } if fresh { c.stats.IncrementMiss() return val, nil } else { // got the result from previous ongoing query c.stats.IncrementHit() } return val, nil } 本文主要介绍了go-zero框架中的 Cache 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"keywords.html":{"url":"keywords.html","title":"高效的关键词替换和敏感词过滤工具","keywords":"","body":"高效的关键词替换和敏感词过滤工具 1. 算法介绍 利用高效的Trie树建立关键词树,如下图所示,然后依次查找字符串中的相连字符是否形成树的一条路径 发现掘金上这篇文章写的比较详细,可以一读,具体原理在此不详述。 2. 关键词替换 支持关键词重叠,自动选用最短的关键词,并且只会对原始字符串只会遍历一次进行匹配替换,如果替换结果中又出现的关键词不会被二次替换(如果业务上有这种可能性,请自行对上一次的替换结果再次执行替换操作),代码示例如下: replacer := stringx.NewReplacer(map[string]string{ \"日本\": \"法国\", \"日本的首都\": \"东京\", \"东京\": \"日本的首都\", }) fmt.Println(replacer.Replace(\"日本的首都是东京\")) 可以得到: ```Plain Text 法国的首都是日本的首都 示例代码见`stringx/replace/replace.go` ## 3. 查找敏感词 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }) keywords := filter.FindKeywords(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(keywords) 可以得到: ```Plain Text [苍井空 日本AV女优 AV演员色情 AV AV演员] ## 4. 敏感词过滤 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }, stringx.WithMask('?')) // 默认替换为* safe, keywords, found := filter.Filter(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(safe) fmt.Println(keywords) fmt.Println(found) 可以得到: Plain Text 日本????兼电视、电影演员。?????女优是xx出道, ??????们最精彩的表演是??????表演 [苍井空 日本AV女优 AV演员色情 AV AV演员] true 示例代码见stringx/filter/filter.go 5. Benchmark Sentences Keywords Regex go-zero 10000 10000 16min10s 27.2ms Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"loadshedding.html":{"url":"loadshedding.html","title":"服务自适应降载保护设计","keywords":"","body":"服务自适应降载保护设计 设计目的 保证系统不被过量请求拖垮 在保证系统稳定的前提下,尽可能提供更高的吞吐量 设计考虑因素 如何衡量系统负载 是否处于虚机或容器内,需要读取cgroup相关负载 用1000m表示100%CPU,推荐使用800m表示系统高负载 尽可能小的Overhead,不显著增加RT 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决 机制设计 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定 当CPU负载大于指定值时触发降载保护机制 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time) 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个 满足以下所有条件则拒绝该请求 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动 averageFlying > max(1, QPS*minRT/1e3) averageFlying = MovingAverage(flying) 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值 取flying值的时候,有三种做法: 请求增加后更新一次averageFlying,见图中橙色曲线 请求结束后更新一次averageFlying,见图中绿色曲线 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying 我们使用的是第二种,这样可以更好的防止抖动,如图: QPS = maxPass * bucketsPerSecond maxPass表示每个有效桶里的成功的requests bucketsPerSecond表示每秒有多少个桶 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求 降载的使用 已经在rest和zrpc框架里增加了可选激活配置 CpuThreshold,如果把值设置为大于0的值,则激活该服务的自动降载机制 如果请求被drop,那么错误日志里会有dropreq关键字 参考资料 滑动平均 Sentinel自适应限流 Kratos自适应限流保护 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mapping.html":{"url":"mapping.html","title":"文本序列化和反序列化","keywords":"","body":"文本序列化和反序列化 go-zero针对文本的序列化和反序列化主要在三个地方使用 http api请求体的反序列化 http api返回体的序列化 配置文件的反序列化 本文假定读者已经定义过api文件以及修改过配置文件,如不熟悉,可参照 快速构建高并发微服务 快速构建高并发微服务 1. http api请求体的反序列化 在反序列化的过程中的针对请求数据的数据格式以及数据校验需求,go-zero实现了自己的一套反序列化机制 1.1 数据格式以订单order.api文件为例 type ( createOrderReq struct { token string `path:\"token\"` // 用户token productId string `json:\"productId\"` // 商品ID num int `json:\"num\"` // 商品数量 } createOrderRes struct { success bool `json:\"success\"` // 是否成功 } findOrderReq struct { token string `path:\"token\"` // 用户token page int `form:\"page\"` // 页数 pageSize int8 `form:\"pageSize\"` // 页大小 } findOrderRes struct { orderInfo []orderInfo `json:\"orderInfo\"` // 商品ID } orderInfo struct { productId string `json:\"productId\"` // 商品ID productName string `json:\"productName\"` // 商品名称 num int `json:\"num\"` // 商品数量 } deleteOrderReq struct { id string `path:\"id\"` } deleteOrderRes struct { success bool `json:\"success\"` // 是否成功 } ) service order { @doc( summary: 创建订单 ) @handler CreateOrderHandler post /order/add/:token(createOrderReq) returns(createOrderRes) @doc( summary: 获取订单 ) @handler FindOrderHandler get /order/find/:token(findOrderReq) returns(findOrderRes) @doc( summary: 删除订单 ) @handler: DeleteOrderHandler delete /order/:id(deleteOrderReq) returns(deleteOrderRes) } http api请求体的反序列化的tag有三种: path:http url 路径中参数反序列化 /order/add/1234567会解析出来token为1234567 form:http form表单反序列化,需要 header头添加 Content-Type: multipart/form-data /order/find/1234567?page=1&pageSize=20会解析出来token为1234567,page为1,pageSize为20 json:http request json body反序列化,需要 header头添加 Content-Type: application/json {\"productId\":\"321\",\"num\":1}会解析出来productId为321,num为1 1.2 数据校验以用户user.api文件为例 type ( createUserReq struct { age int8 `json:\"age,default=20,range=(12:100]\"` // 年龄 name string `json:\"name\"` // 名字 alias string `json:\"alias,optional\"` // 别名 sex string `json:\"sex,options=male|female\"` // 性别 avatar string `json:\"avatar,default=default.png\"` // 头像 } createUserRes struct { success bool `json:\"success\"` // 是否成功 } ) service user { @doc( summary: 创建订单 ) @handler CreateUserHandler post /user/add(createUserReq) returns(createUserRes) } 数据校验有很多种方式,包括以下但不限: age:默认不输入为20,输入则取值范围为(12:100],前开后闭 name:必填,不可为空 alias:选填,可为空 sex:必填,取值为male或female avatar:选填,默认为default.png 更多详情参见unmarshaler_test.go 2. http api返回体的序列化 使用官方默认的encoding/json包序列化,在此不再累赘 3. 配置文件的反序列化 配置文件的反序列化和http api请求体的反序列化使用同一套解析规则,可参照http api请求体的反序列化 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mapreduce.html":{"url":"mapreduce.html","title":"并发处理工具 MapReduce","keywords":"","body":"通过MapReduce降低服务响应时间 在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了 WaitGroup 工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错 基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,该工具开箱即用,不需要做什么初始化,我们通过下图看下使用MapReduce和没使用的耗时对比: 相同的依赖,串行处理的话需要200ms,使用MapReduce后的耗时等于所有依赖中最大的耗时为100ms,可见MapReduce可以大大降低服务耗时,而且随着依赖的增加效果就会越明显,减少处理耗时的同时并不会增加服务器压力 并发处理工具MapReduce MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想 go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能 我们通过几个示例来演示MapReduce的用法 MapReduce主要有三个参数,第一个参数为generate用以生产数据,第二个参数为mapper用以对数据进行处理,第三个参数为reducer用以对mapper后的数据做聚合返回,还可以通过opts选项设置并发处理的线程数量 场景一: 某些功能的结果往往需要依赖多个服务,比如商品详情的结果往往会依赖用户服务、库存服务、订单服务等等,一般被依赖的服务都是以rpc的形式对外提供,为了降低依赖的耗时我们往往需要对依赖做并行处理 func productDetail(uid, pid int64) (*ProductDetail, error) { var pd ProductDetail err := mr.Finish(func() (err error) { pd.User, err = userRpc.User(uid) return }, func() (err error) { pd.Store, err = storeRpc.Store(pid) return }, func() (err error) { pd.Order, err = orderRpc.Order(pid) return }) if err != nil { log.Printf(\"product detail error: %v\", err) return nil, err } return &pd, nil } 该示例中返回商品详情依赖了多个服务获取数据,因此做并发的依赖处理,对接口的性能有很大的提升 场景二: 很多时候我们需要对一批数据进行处理,比如对一批用户id,效验每个用户的合法性并且效验过程中有一个出错就认为效验失败,返回的结果为效验合法的用户id func checkLegal(uids []int64) ([]int64, error) { r, err := mr.MapReduce(func(source chan 该示例中,如果check过程出现错误则通过cancel方法结束效验过程,并返回error整个效验过程结束,如果某个uid效验结果为false则最终结果不返回该uid MapReduce使用注意事项 mapper和reducer中都可以调用cancel,参数为error,调用后立即返回,返回结果为nil, error mapper中如果不调用writer.Write则item最终不会被reducer聚合 reducer中如果不调用writer.Wirte则返回结果为nil, ErrReduceNoOutput reducer为单线程,所有mapper出来的结果在这里串行聚合 实现原理分析: MapReduce中首先通过buildSource方法通过执行generate(参数为无缓冲channel)产生数据,并返回无缓冲的channel,mapper会从该channel中读取数据 func buildSource(generate GenerateFunc) chan interface{} { source := make(chan interface{}) go func() { defer close(source) generate(source) }() return source } 在MapReduceWithSource方法中定义了cancel方法,mapper和reducer中都可以调用该方法,调用后主线程收到close信号会立马返回 cancel := once(func(err error) { if err != nil { retErr.Set(err) } else { // 默认的error retErr.Set(ErrCancelWithNil) } drain(source) // 调用close(ouput)主线程收到Done信号,立马返回 finish() }) 在mapperDispatcher方法中调用了executeMappers,executeMappers消费buildSource产生的数据,每一个item都会起一个goroutine单独处理,默认最大并发数为16,可以通过WithWorkers进行设置 var wg sync.WaitGroup defer func() { wg.Wait() // 保证所有的item都处理完成 close(collector) }() pool := make(chan lang.PlaceholderType, workers) writer := newGuardedWriter(collector, done) // 将mapper处理完的数据写入collector for { select { case reducer单goroutine对数mapper写入collector的数据进行处理,如果reducer中没有手动调用writer.Write则最终会执行finish方法对output进行close避免死锁 go func() { defer func() { if r := recover(); r != nil { cancel(fmt.Errorf(\"%v\", r)) } else { finish() } }() reducer(collector, writer, cancel) }() 在该工具包中还提供了许多针对不同业务场景的方法,实现原理与MapReduce大同小异,感兴趣的同学可以查看源码学习 MapReduceVoid 功能和MapReduce类似但没有结果返回只返回error Finish 处理固定数量的依赖,返回error,有一个error立即返回 FinishVoid 和Finish方法功能类似,没有返回值 Map 只做generate和mapper处理,返回channel MapVoid 和Map功能类似,无返回 本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"metric.html":{"url":"metric.html","title":"基于prometheus的微服务指标监控","keywords":"","body":"基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero中目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"sharedcalls.html":{"url":"sharedcalls.html","title":"防止缓存击穿之进程内共享调用","keywords":"","body":"防止缓存击穿之进程内共享调用 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等。 本文主要讲述进程内共享调用神器SharedCalls(v1.2.0改名为SingleFlight ) 使用场景 并发场景下,可能会有多个线程(协程)同时请求同一份资源,如果每个请求都要走一遍资源的请求过程,除了比较低效之外,还会对资源服务造成并发的压力。举一个具体例子,比如缓存失效,多个请求同时到达某服务请求某资源,该资源在缓存中已经失效,此时这些请求会继续访问DB做查询,会引起数据库压力瞬间增大。而使用SharedCalls可以使得同时多个请求只需要发起一次拿结果的调用,其他请求\"坐享其成\",这种设计有效减少了资源服务的并发压力,可以有效防止缓存击穿。 高并发场景下,当某个热点key缓存失效后,多个请求会同时从数据库加载该资源,并保存到缓存,如果不做防范,可能会导致数据库被直接打死。针对这种场景,go-zero框架中已经提供了实现,具体可参看sqlc和mongoc等实现代码。 为了简化演示代码,我们通过多个线程同时去获取一个id来模拟缓存的场景。如下: func main() { const round = 5 var wg sync.WaitGroup barrier := syncx.NewSharedCalls() wg.Add(round) for i := 0; i 运行,打印结果为: 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 可以看出,只要是同一个key上的同时发起的请求,都会共享同一个结果,对获取DB数据进缓存等场景特别有用,可以有效防止缓存击穿。 关键源码分析 SharedCalls interface提供了Do和DoEx两种方法的抽象 // SharedCalls接口提供了Do和DoEx两种方法 type SharedCalls interface { Do(key string, fn func() (interface{}, error)) (interface{}, error) DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error) } SharedCalls interface的具体实现sharedGroup // call代表对指定资源的一次请求 type call struct { wg sync.WaitGroup // 用于协调各个请求goroutine之间的资源共享 val interface{} // 用于保存请求的返回值 err error // 用于保存请求过程中发生的错误 } type sharedGroup struct { calls map[string]*call lock sync.Mutex } sharedGroup的Do方法 key参数:可以理解为资源的唯一标识。 fn参数:真正获取资源的方法。 处理过程分析: // 当多个请求同时使用Do方法请求资源时 func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) { // 先申请加锁 g.lock.Lock() // 根据key,获取对应的call结果,并用变量c保存 if c, ok := g.calls[key]; ok { // 拿到call以后,释放锁,此处call可能还没有实际数据,只是一个空的内存占位 g.lock.Unlock() // 调用wg.Wait,判断是否有其他goroutine正在申请资源,如果阻塞,说明有其他goroutine正在获取资源 c.wg.Wait() // 当wg.Wait不再阻塞,表示资源获取已经结束,可以直接返回结果 return c.val, c.err } // 没有拿到结果,则调用makeCall方法去获取资源,注意此处仍然是锁住的,可以保证只有一个goroutine可以调用makecall c := g.makeCall(key, fn) // 返回调用结果 return c.val, c.err } sharedGroup的DoEx方法 和Do方法类似,只是返回值中增加了布尔值表示值是调用makeCall方法直接获取的,还是取的共享成果 func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) { g.lock.Lock() if c, ok := g.calls[key]; ok { g.lock.Unlock() c.wg.Wait() return c.val, false, c.err } c := g.makeCall(key, fn) return c.val, true, c.err } sharedGroup的makeCall方法 该方法由Do和DoEx方法调用,是真正发起资源请求的方法。 // 进入makeCall的一定只有一个goroutine,因为要拿锁锁住的 func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call { // 创建call结构,用于保存本次请求的结果 c := new(call) // wg加1,用于通知其他请求资源的goroutine等待本次资源获取的结束 c.wg.Add(1) // 将用于保存结果的call放入map中,以供其他goroutine获取 g.calls[key] = c // 释放锁,这样其他请求的goroutine才能获取call的内存占位 g.lock.Unlock() defer func() { // delete key first, done later. can't reverse the order, because if reverse, // another Do call might wg.Wait() without get notified with wg.Done() g.lock.Lock() delete(g.calls, key) g.lock.Unlock() // 调用wg.Done,通知其他goroutine可以返回结果,这样本批次所有请求完成结果的共享 c.wg.Done() }() // 调用fn方法,将结果填入变量c中 c.val, c.err = fn() return c } 最后 本文主要介绍了go-zero框架中的 SharedCalls工具,对其应用场景和关键代码做了简单的梳理,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"sql-cache.html":{"url":"sql-cache.html","title":"DB缓存机制","keywords":"","body":"DB缓存机制 QueryRowIndex 没有查询条件到Primary映射的缓存 通过查询条件到DB去查询行记录,然后 把Primary到行记录的缓存写到redis里 把查询条件到Primary的映射保存到redis里,框架的Take方法自动做了 可能的过期顺序 查询条件到Primary的映射缓存未过期 Primary到行记录的缓存未过期 直接返回缓存行记录 Primary到行记录的缓存已过期 通过Primary到DB获取行记录,并写入缓存 此时存在的问题是,查询条件到Primary的缓存可能已经快要过期了,短时间内的查询又会触发一次数据库查询 要避免这个问题,可以让上面粗体部分第一个过期时间略长于第二个,比如5秒 查询条件到Primary的映射缓存已过期,不管Primary到行记录的缓存是否过期 查询条件到Primary的映射会被重新获取,获取过程中会自动写入新的Primary到行记录的缓存,这样两种缓存的过期时间都是刚刚设置 有查询条件到Primary映射的缓存 没有Primary到行记录的缓存 通过Primary到DB查询行记录,并写入缓存 有Primary到行记录的缓存 直接返回缓存结果 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"zrpc.html":{"url":"zrpc.html","title":"zrpc 使用介绍","keywords":"","body":"企业级RPC框架zRPC 近期比较火的开源项目go-zero是一个集成了各种工程实践的包含了Web和RPC协议的功能完善的微服务框架,今天我们就一起来分析一下其中的RPC部分zRPC。 zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。 zRPC初探 zRPC支持直连和基于etcd服务发现两种方式,我们以基于etcd做服务发现为例演示zRPC的基本使用: 配置 创建hello.yaml配置文件,配置如下: Name: hello.rpc // 服务名 ListenOn: 127.0.0.1:9090 // 服务监听地址 Etcd: Hosts: - 127.0.0.1:2379 // etcd服务地址 Key: hello.rpc // 服务注册key 创建proto文件 创建hello.proto文件,并生成对应的go代码 syntax = \"proto3\"; package pb; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 生成go代码 protoc --go_out=plugins=grpc:. hello.proto Server端 package main import ( \"context\" \"flag\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type Config struct { zrpc.RpcServerConf } var cfgFile = flag.String(\"f\", \"./hello.yaml\", \"cfg file\") func main() { flag.Parse() var cfg Config conf.MustLoad(*cfgFile, &cfg) srv, err := zrpc.NewServer(cfg.RpcServerConf, func(s *grpc.Server) { pb.RegisterGreeterServer(s, &Hello{}) }) if err != nil { log.Fatal(err) } srv.Start() } type Hello struct{} func (h *Hello) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: \"hello \" + in.Name}, nil } Client端 package main import ( \"context\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/discov\" \"github.com/zeromicro/go-zero/zrpc\" ) func main() { client := zrpc.MustNewClient(zrpc.RpcClientConf{ Etcd: discov.EtcdConf{ Hosts: []string{\"127.0.0.1:2379\"}, Key: \"hello.rpc\", }, }) conn := client.Conn() hello := pb.NewGreeterClient(conn) reply, err := hello.SayHello(context.Background(), &pb.HelloRequest{Name: \"go-zero\"}) if err != nil { log.Fatal(err) } log.Println(reply.Message) } 启动服务,查看服务是否注册: ETCDCTL_API=3 etcdctl get hello.rpc --prefix 显示服务已经注册: hello.rpc/7587849401504590084 127.0.0.1:9090 运行客户端即可看到输出: hello go-zero 这个例子演示了zRPC的基本使用,可以看到通过zRPC构建RPC服务非常简单,只需要很少的几行代码,接下来我们继续进行探索 zRPC原理分析 下图展示zRPC的架构图和主要组成部分 zRPC主要有以下几个模块组成: discov: 服务发现模块,基于etcd实现服务发现功能 resolver: 服务注册模块,实现了gRPC的resolver.Builder接口并注册到gRPC interceptor: 拦截器,对请求和响应进行拦截处理 balancer: 负载均衡模块,实现了p2c负载均衡算法,并注册到gRPC client: zRPC客户端,负责发起请求 server: zRPC服务端,负责处理请求 这里介绍了zRPC的主要组成模块和每个模块的主要功能,其中resolver和balancer模块实现了gRPC开放的接口,实现了自定义的resolver和balancer,拦截器模块是整个zRPC的功能重点,自适应降载、自适应熔断、prometheus服务指标收集等功能都在这里实现 Interceptor模块 gRPC提供了拦截器功能,主要是对请求前后进行额外处理的拦截操作,其中拦截器包含客户端拦截器和服务端拦截器,又分为一元(Unary)拦截器和流(Stream)拦截器,这里我们主要讲解一元拦截器,流拦截器同理。 客户端拦截器定义如下: type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error 其中method为方法名,req,reply分别为请求和响应参数,cc为客户端连接对象,invoker参数是真正执行rpc方法的handler其实在拦截器中被调用执行 服务端拦截器定义如下: type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) 其中req为请求参数,info中包含了请求方法属性,handler为对server端方法的包装,也是在拦截器中被调用执行 zRPC中内置了丰富的拦截器,其中包括自适应降载、自适应熔断、权限验证、prometheus指标收集等等,由于拦截器较多,篇幅有限没法所有的拦截器给大家一一解析,这里我们主要分析两个,自适应熔断和prometheus服务监控指标收集: 内置拦截器分析 自适应熔断(breaker) 当客户端向服务端发起请求,客户端会记录服务端返回的错误,当错误达到一定的比例,客户端会自行的进行熔断处理,丢弃掉一定比例的请求以保护下游依赖,且可以自动恢复。zRPC中自适应熔断遵循《Google SRE》中过载保护策略,算法如下: requests: 总请求数量 accepts: 正常请求数量 K: 倍值 (Google SRE推荐值为2) 可以通过修改K的值来修改熔断发生的激进程度,降低K的值会使得自适应熔断算法更加激进,增加K的值则自适应熔断算法变得不再那么激进 熔断拦截器定义如下: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // target + 方法名 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正执行调用 return invoker(ctx, method, req, reply, cc, opts...) }, codes.Acceptable) } accept方法实现了Google SRE过载保护算法,判断否进行熔断 func (b *googleBreaker) accept() error { // accepts为正常请求数,total为总请求数 accepts, total := b.history() weightedAccepts := b.k * float64(accepts) // 算法实现 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio doReq方法首先判断是否熔断,满足条件直接返回error(circuit breaker is open),不满足条件则对请求数进行累加 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 此处执行RPC请求 err := req() // 正常请求total和accepts都会加1 if acceptable(err) { b.markSuccess() } else { // 请求失败只有total会加1 b.markFailure() } return err } prometheus指标收集 服务监控是了解服务当前运行状态以及变化趋势的重要手段,监控依赖于服务指标的收集,通过prometheus进行监控指标的收集是业界主流方案,zRPC中也采用了prometheus来进行指标的收集 prometheus拦截器定义如下: 这个拦截器主要是对服务的监控指标进行收集,这里主要是对RPC方法的耗时和调用错误进行收集,这里主要使用了Prometheus的Histogram和Counter数据类型 func UnaryPrometheusInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) ( interface{}, error) { // 执行前记录一个时间 startTime := timex.Now() resp, err := handler(ctx, req) // 执行后通过Since算出执行该调用的耗时 metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), info.FullMethod) // 方法对应的错误码 metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err)))) return resp, err } } 添加自定义拦截器 除了内置了丰富的拦截器之外,zRPC同时支持添加自定义拦截器 Client端通过AddInterceptor方法添加一元拦截器: func (rc *RpcClient) AddInterceptor(interceptor grpc.UnaryClientInterceptor) { rc.client.AddInterceptor(interceptor) } Server端通过AddUnaryInterceptors方法添加一元拦截器: func (rs *RpcServer) AddUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) { rs.server.AddUnaryInterceptors(interceptors...) } resolver模块 zRPC服务注册架构图: zRPC中自定义了resolver模块,用来实现服务的注册功能。zRPC底层依赖gRPC,在gRPC中要想自定义resolver需要实现resolver.Builder接口: type Builder interface { Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error) Scheme() string } 其中Build方法返回Resolver,Resolver定义如下: type Resolver interface { ResolveNow(ResolveNowOptions) Close() } 在zRPC中定义了两种resolver,direct和discov,这里我们主要分析基于etcd做服务发现的discov,自定义的resolver需要通过gRPC提供了Register方法进行注册代码如下: func RegisterResolver() { resolver.Register(&dirBuilder) resolver.Register(&disBuilder) } 当我们启动我们的zRPC Server的时候,调用Start方法,会像etcd中注册对应的服务地址: func (ags keepAliveServer) Start(fn RegisterFn) error { // 注册服务地址 if err := ags.registerEtcd(); err != nil { return err } // 启动服务 return ags.Server.Start(fn) } 当我们启动zRPC客户端的时候,在gRPC内部会调用我们自定义resolver的Build方法,zRPC通过在Build方法内调用执行了resolver.ClientConn的UpdateState方法,该方法会把服务地址注册到gRPC客户端内部: func (d *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) ( resolver.Resolver, error) { hosts := strings.FieldsFunc(target.Authority, func(r rune) bool { return r == EndpointSepChar }) // 服务发现 sub, err := discov.NewSubscriber(hosts, target.Endpoint) if err != nil { return nil, err } update := func() { var addrs []resolver.Address for _, val := range subset(sub.Values(), subsetSize) { addrs = append(addrs, resolver.Address{ Addr: val, }) } // 向gRPC注册服务地址 cc.UpdateState(resolver.State{ Addresses: addrs, }) } // 监听 sub.AddListener(update) update() // 返回自定义的resolver.Resolver return &nopResolver{cc: cc}, nil } 在discov中,通过调用load方法从etcd中获取指定服务的所有地址: func (c *cluster) load(cli EtcdClient, key string) { var resp *clientv3.GetResponse for { var err error ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout) // 从etcd中获取指定服务的所有地址 resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix()) cancel() if err == nil { break } logx.Error(err) time.Sleep(coolDownInterval) } var kvs []KV c.lock.Lock() for _, ev := range resp.Kvs { kvs = append(kvs, KV{ Key: string(ev.Key), Val: string(ev.Value), }) } c.lock.Unlock() c.handleChanges(key, kvs) } 并通过watch监听服务地址的变化: func (c *cluster) watch(cli EtcdClient, key string) { rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix()) for { select { case wresp, ok := 这部分主要介绍了zRPC中是如何自定义的resolver,以及基于etcd的服务发现原理,通过这部分的介绍大家可以了解到zRPC内部服务注册发现的原理,源代码比较多只是粗略的从整个流程上进行了分析,如果大家对zRPC的源码比较感兴趣可以自行进行学习 balancer模块 负载均衡原理图: 避免过载是负载均衡策略的一个重要指标,好的负载均衡算法能很好的平衡服务端资源。常用的负载均衡算法有轮训、随机、Hash、加权轮训等。但为了应对各种复杂的场景,简单的负载均衡算法往往表现的不够好,比如轮训算法当服务响应时间变长就很容易导致负载不再平衡, 因此zRPC中自定义了默认负载均衡算法P2C(Power of Two Choices),和resolver类似,要想自定义balancer也需要实现gRPC定义的balancer.Builder接口,由于和resolver类似这里不再带大家一起分析如何自定义balancer,感兴趣的朋友可以查看gRPC相关的文档来进行学习 注意,zRPC是在客户端进行负载均衡,常见的还有通过nginx中间代理的方式 zRPC框架中默认的负载均衡算法为P2C,该算法的主要思想是: 从可用节点列表中做两次随机选择操作,得到节点A、B 比较A、B两个节点,选出负载最低的节点作为被选中的节点 伪代码如下: 主要算法逻辑在Pick方法中实现: func (p *p2cPicker) Pick(ctx context.Context, info balancer.PickInfo) ( conn balancer.SubConn, done func(balancer.DoneInfo), err error) { p.lock.Lock() defer p.lock.Unlock() var chosen *subConn switch len(p.conns) { case 0: return nil, nil, balancer.ErrNoSubConnAvailable case 1: chosen = p.choose(p.conns[0], nil) case 2: chosen = p.choose(p.conns[0], p.conns[1]) default: var node1, node2 *subConn for i := 0; i = a { b++ } // 随机获取所有节点中的两个节点 node1 = p.conns[a] node2 = p.conns[b] // 效验节点是否健康 if node1.healthy() && node2.healthy() { break } } // 选择其中一个节点 chosen = p.choose(node1, node2) } atomic.AddInt64(&chosen.inflight, 1) atomic.AddInt64(&chosen.requests, 1) return chosen.conn, p.buildDoneFunc(chosen), nil } choose方法对随机选择出来的节点进行负载比较从而最终确定选择哪个节点 func (p *p2cPicker) choose(c1, c2 *subConn) *subConn { start := int64(timex.Now()) if c2 == nil { atomic.StoreInt64(&c1.pick, start) return c1 } if c1.load() > c2.load() { c1, c2 = c2, c1 } pick := atomic.LoadInt64(&c2.pick) if start-pick > forcePick && atomic.CompareAndSwapInt64(&c2.pick, pick, start) { return c2 } else { atomic.StoreInt64(&c1.pick, start) return c1 } } 上面主要介绍了zRPC默认负载均衡算法的设计思想和代码实现,那自定义的balancer是如何注册到gRPC的呢,resolver提供了Register方法来进行注册,同样balancer也提供了Register方法来进行注册: func init() { balancer.Register(newBuilder()) } func newBuilder() balancer.Builder { return base.NewBalancerBuilder(Name, new(p2cPickerBuilder)) } 注册balancer之后gRPC怎么知道使用哪个balancer呢?这里我们需要使用配置项进行配置,在NewClient的时候通过grpc.WithBalancerName方法进行配置: func NewClient(target string, opts ...ClientOption) (*client, error) { var cli client opts = append(opts, WithDialOption(grpc.WithBalancerName(p2c.Name))) if err := cli.dial(target, opts...); err != nil { return nil, err } return &cli, nil } 这部分主要介绍了zRPC中内中的负载均衡算法的实现原理以及具体的实现方式,之后介绍了zRPC是如何注册自定义的balancer以及如何选择自定义的balancer,通过这部分大家应该对负载均衡有了更进一步的认识 总结 首先,介绍了zRPC的基本使用方法,可以看到zRPC使用非常简单,只需要少数几行代码就可以构建高性能和自带服务治理能力的RPC服务,当然这里没有面面俱到的介绍zRPC的基本使用,大家可以查看相关文档进行学习 接着,介绍了zRPC的几个重要组成模块以及其实现原理,并分析了部分源码。拦截器模块是整个zRPC的重点,其中内置了丰富的功能,像熔断、监控、降载等等也是构建高可用微服务必不可少的。resolver和balancer模块自定义了gRPC的resolver和balancer,通过该部分可以了解到整个服务注册与发现的原理以及如何构建自己的服务发现系统,同时自定义负载均衡算法也变得不再神秘 最后,zRPC是一个经历过各种工程实践的RPC框架,不论是想要用于生产还是学习其中的设计模式都是一个不可多得的开源项目。希望通过这篇文章的介绍大家能够进一步了解zRPC Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-looklook.html":{"url":"go-zero-looklook.html","title":"使用go-zero开发一个旅游系统go-zero-looklook","keywords":"","body":"使用go-zero开发一个旅游系统go-zero-looklook 因为大家都在说目前go-zero没有一个完整的项目例子,本人接触go-zero可能比较早,go-zero在大约1000start左右我就在用了,后来跟go-zero作者加了微信也熟悉了,go-zero作者非常热心以及耐心的帮我解答了很多问题,我也想积极帮助go-zero推广社区,基本是在社区群内回答大家相关的问题,因为在这个过程中发现很多人觉得go-zero没有一个完整的项目例子,作为想推动社区的一员,索性我就把内部项目删减了一些关键东西,就搞了个可用版本开源出来,主要技术栈包含如下: go-zero nginx网关 filebeat kafka go-stash elasticsearch kibana prometheus grafana jaeger go-queue asynq asynqmon dtm docker docker-compose mysql redis 项目地址 https://github.com/Mikaelemmmm/go-zero-looklook 项目文档 https://github.com/Mikaelemmmm/go-zero-looklook/tree/main/doc 项目简介 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中间件,所用到的技术栈基本是go-zero项目组的自研组件,基本是go-zero全家桶了 另外,前端是小程序,本项目已经对接好了小程序授权登录 以及 微信支付了 ,前端看看后面是否能开源吧 项目目录结构如下: app:所有业务代码包含api、rpc以及mq(消息队列、延迟队列、定时任务) common:通用组件 error、middleware、interceptor、tool、ctxdata等 data:该项目包含该目录依赖所有中间件(mysql、es、redis、grafana等)产生的数据,此目录下的所有内容应该在git忽略文件中,不需要提交。 deploy: filebeat: docker部署filebeat配置 go-stash:go-stash配置 nginx: nginx网关配置 prometheus : prometheus配置 script: gencode:生成api、rpc,以及创建kafka语句,复制粘贴使用 mysql:生成model的sh工具 goctl: 该项目goctl的template,goctl生成自定义代码模版,tempalte用法可参考go-zero文档,复制到家目录下.goctl即可, 该项目用到goctl版本是v1.2.3 doc : 该项目系列文档 系统架构图 业务架构图 网关 nginx做网关,使用nginx的auth模块,调用后端的identity服务统一鉴权,业务内部不鉴权,如果涉及到业务资金比较多也可以在业务中进行二次鉴权,为了安全嘛。 另外,很多同学觉得nginx做网关不太好,这块原理基本一样,可以自行替换成apisix、kong等 开发模式 本项目使用的是微服务开发,api (http) + rpc(grpc) , api充当聚合服务,复杂、涉及到其他业务调用的统一写在rpc中,如果一些不会被其他服务依赖使用的简单业务,可以直接写在api的logic中 日志 关于日志,统一使用filebeat收集,上报到kafka中,由于logstash懂得都懂,资源占用太夸张了,这里使用了go-stash替换了logstash 链接:https://github.com/kevwan/go-stash , go-stash是由go-zero开发团队开发的,性能很高不占资源,主要代码量没多少,只需要配置就可以使用,很简单。它是吧kafka数据源同步到elasticsearch中,默认不支持elasticsearch账号密码,我fork了一份修改了一下,很简单支持了账号、密码 监控 监控采用prometheus,这个go-zero原生支持,只需要配置就可以了,这里可以看项目中的配置 链路追踪 go-zero默认jaeger、zipkin支持,只需要配置就可以了,可以看配置 消息队列 消息队列使用的是go-zero开发团队开发的go-queue,链接:https://github.com/zeromicro/go-queue 这里使用可kq,kq是基于kafka做的高性能消息队列 通用go-queue中也有dq,是延迟队列,不过当前项目没有使用dq 延迟队列、定时任务 延迟队列、定时任务本项目使用的是asynq , google团队给予redis开发的简单中间件, 当然了asynq也支持消息队列,你也可也把kq消息队列替换成这个,毕竟只需要redis不需要在去维护一个kafka也是不错的 链接:https://github.com/hibiken/asynq 分布式事务 分布式事务准备使用的是dtm, 嗯 ,很舒服,之前我写过一篇 \"go-zero对接分布式事务dtm保姆式教程\" 链接地址:https://github.com/Mikaelemmmm/gozerodtm , 本项目目前还未使用到,后续准备直接集成就好了,如果读者使用直接去看那个源码就行了 部署 部署的话,目前这个直接使用docker可以部署整套技术栈,如果上k8s的话 ,最简单直接用阿里云的吧 我说下思路,这个后续会出一个基于阿里云效的部署到k8s文档教程,自己搭建一个gitlab、jenkins、harbor去做的话太费时间了 1、将代码放在阿里云效(当然你整到gitlab也行) 2、在阿里云效创建流水线,基本是一个服务一个流水线了 3、流水线步骤 : ​ 拉取代码--->ci检测(这里可以省略哈,自己看着办)--->构建镜像(go-zero官方有Dockerfile还有教程,别告诉我不会)-->推送到阿里云镜像服务--->使用kubectl去阿里云k8s拉取镜像(ack、ask都行,ask无法使用daemonset 不能用filebeat)---->ok了 另外, 如果你想自己基于gitlab、jenkins、harbor去做的话,嗯 自己去找运维弄吧,我之前也写过一个教程,有空在整吧老哥们!! Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"faq.html":{"url":"faq.html","title":"FAQ","keywords":"","body":"常见问题集合 goctl安装了执行命令却提示 command not found: goctl 字样。 如果你通过 go get 方式安装,那么 goctl 应该位于 $GOPATH 中, 你可以通过 go env GOPATH 查看完整路径,不管你的 goctl 是在 $GOPATH中, 还是在其他目录,出现上述问题的原因就是 goctl 所在目录不在 PATH (环境变量)中所致。 rpc怎么调用 该问题可以参考快速开始中的rpc编写与调用介绍,其中有rpc调用的使用逻辑。 proto使用了import,goctl命令需要怎么写。 goctl 对于import的proto指定 BasePath 提供了 protoc 的flag映射,即 --proto_path, -I, goctl 会将此flag值传递给 protoc. 假设 base.proto 的被main proto 引入了,为什么不生能生成base.pb.go。 对于 base.proto 这种类型的文件,一般都是开发者有message复用的需求,他的来源不止有开发者自己编写的proto文件, 还有可能来源于 google.golang.org/grpc 中提供的一些基本的proto,比如 google/protobuf/any.proto, 如果由 goctl 来生成,那么就失去了集中管理这些proto的意义。 model怎么控制缓存时间 在 sqlc.NewNodeConn 的时候可以通过可选参数 cache.WithExpiry 传递,如缓存时间控制为1天,代码如下: sqlc.NewNodeConn(conn,redis,cache.WithExpiry(24*time.Hour)) jwt鉴权怎么实现 请参考jwt鉴权 api中间件怎么使用 请参考中间件 怎么关闭输出的统计日志(stat)? logx.DisableStat() rpc直连与服务发现连接模式写法 // mode1: 集群直连 // conf:=zrpc.NewDirectClientConf([]string{\"ip:port\"},\"app\",\"token\") // mode2: etcd 服务发现 // conf:=zrpc.NewEtcdClientConf([]string{\"ip:port\"},\"key\",\"app\",\"token\") // client, _ := zrpc.NewClient(conf) // mode3: ip直连mode // client, _ := zrpc.NewClientWithTarget(\"127.0.0.1:8888\") grpc 客户端设置消息大小限制 修改grpc消息大小限制,需要 服务端 和 客户端 都设置 // 需要 grpc 方法里面添加配置项:grpc.MaxCallRecvMsgSize(bytes int)、grpc.MaxCallSendMsgSize(bytes int) // 示例如下:设置 UserRpc.List 列表消息大小限制为 8MB // l.svcCtx.UserRpc.List(l.ctx, &listReq, grpc.MaxCallRecvMsgSize(1024*1024*8), grpc.MaxCallSendMsgSize(1024*1024*8)) faq会不定期更新大家遇到的问题,也欢迎大家把常见问题通过pr写在这里。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "}} \ No newline at end of file +{"./":{"url":"./","title":"简介","keywords":"","body":" go-zero 缩短从需求到上线的距离 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 使用 go-zero 的好处: 轻松获得支撑千万日活服务的稳定性 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码 微服务治理中间件可无缝集成到其它现有框架使用 极简的 API 描述,一键生成各端代码 自动校验客户端请求参数合法性 大量微服务治理和并发工具包 1. go-zero 框架背景 18 年初,我们决定从 Java+MongoDB 的单体架构迁移到微服务架构,经过仔细思考和对比,我们决定: 基于 Go 语言 高效的性能 简洁的语法 广泛验证的工程效率 极致的部署体验 极低的服务端资源成本 自研微服务框架 有过很多微服务框架自研经验 需要有更快速的问题定位能力 更便捷的增加新特性 2. go-zero 框架设计思考 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 我们经历不到半年时间,彻底完成了从 Java+MongoDB 到 Golang+MySQL 为主的微服务体系迁移,并于 18 年 8 月底完全上线,稳定保障了业务后续迅速增长,确保了整个服务的高可用。 3. go-zero 项目实现和特点 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: 觉得不错的话,别忘 star 👏 4. Installation 在项目目录下通过如下命令安装: GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero 5. Quick Start 完整示例请查看 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 安装 goctl 工具 goctl 读作 go control,不要读成 go C-T-L。goctl 的意思是不要被代码控制,而是要去控制它。其中的 go 不是指 golang。在设计 goctl 之初,我就希望通过 她 来解放我们的双手👈 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 如果使用 go1.16 版本, 可以使用 go install 命令安装 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 确保 goctl 可执行 快速生成 api 服务 goctl api new greet cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: curl -i http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 22 Oct 2020 14:03:18 GMT Content-Length: 14 {\"message\":\"\"} 编写业务代码: api 文件定义了服务对外暴露的路由,可参考 api 规范 可以在 servicecontext.go 里面传递依赖给 logic,比如 mysql, redis 等 在 api 定义的 get/post/put/delete 等请求对应的 logic 里增加业务处理逻辑 可以根据 api 文件生成前端需要的 Java, TypeScript, Dart, JavaScript 代码 goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 6. Benchmark 测试代码见这里 7. 文档 API 文档 goctl 使用帮助 awesome 系列(更多文章见『微服务实践』公众号) 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 精选 goctl 插件 插件 用途 goctl-swagger 一键生成 api 的 swagger 文档 goctl-android 生成 java (android) 端 http client 请求代码 goctl-go-compact 合并 api 里同一个 group 里的 handler 到一个 go 文件 8. 微信公众号 go-zero 相关文章都会在 微服务实践 公众号整理呈现,欢迎扫码关注,也可以通过公众号私信我 👏 9. 微信交流群 如果文档中未能覆盖的任何疑问,欢迎您在群里提出,我们会尽快答复。 您可以在群内提出使用中需要改进的地方,我们会考虑合理性并尽快修改。 如果您发现 bug 请及时提 issue,我们会尽快确认并修改。 为了防止广告用户、识别技术同行,请 star 后加我时注明 github 当前 star 数,我再拉进 go-zero 群,感谢! 加我之前有劳点一下 star,一个小小的 star 是作者们回答海量问题的动力🤝 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"about-us.html":{"url":"about-us.html","title":"关于我们","keywords":"","body":"关于我们 go-zero go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 go-zero作者 万俊峰,七牛云技术副总裁,拥有14年研发团队管理经验,16年架构设计经验,20年工程实战经验,负责过多个大型项目的架构设计,曾多次合伙创业(被收购),阿里云MVP,ArchSummit全球架构师峰会明星讲师,GopherChina大会主持人 & 金牌讲师,QCon+ Go语言出品人兼讲师,腾讯云开发者大会讲师。 go-zero社区 我们目前拥有7000多人的社区成员,在这里,你可以和大家讨论任何关于go-zero的技术,问题反馈,获取最新的go-zero信息,以及各位大佬每天分享的技术心得。 go-zero社区群 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"join-us.html":{"url":"join-us.html","title":"加入我们","keywords":"","body":"加入我们 概要 go-zero 是一个基于MIT License 的开源项目,大家在使用中发现bug,有新的特性等,均可以参与到go-zero的贡献中来,我们非常欢迎大家的积极参与,也会最快响应大家提出的各种问题,pr等。 贡献形式 Pull Request Issue 贡献须知 go-zero 的Pull request中的代码需要满足一定规范 命名规范,请阅读命名规范 以英文注释为主 pr时备注好功能特性,描述需要清晰,简洁 增加单元测试覆盖率达80%+ 贡献代码(pr) 进入go-zero 项目,fork一份go-zero 项目到自己的github仓库中。 回到自己的github主页,找到xx/go-zero项目,其中xx为你的用户名,如anqiansong/go-zero 克隆代码到本地 开发代码,push到自己的github仓库 进入自己的github中go-zero项目,点击浮层上的的【Pull requests】进入Compare页面。 base repository选择zeromicro/go-zero base:master,head repository选择xx/go-zero compare:$branch ,$branch为你开发的分支,如图: 点击【Create pull request】即可实现pr申请 确认pr是否提交成功,进入go-zero 的Pull requests 查看,应该有自己提交的记录,名称为你的开发时的分支名称 Issue 在我们的社区中,有很多伙伴会积极的反馈一些go-zero使用过程中遇到的问题,由于社区人数较多,我们虽然会实时的关注社区动态,但大家问题反馈过来都是随机的,当我们团队还在解决某一个伙伴提出的问题时,另外的问题也反馈上来,可能会导致团队会很容易忽略掉,为了能够一一的解决大家的问题,我们强烈建议大家通过issue的方式来反馈问题,包括但不限于bug,期望的新功能特性等,我们在实现某一个新特性时也会在issue中体现,大家在这里也能够在这里获取到go-zero的最新动向,也欢迎大家来积极的参与讨论。 怎么提Issue 点击这里 进入go-zero的Issue页面或者直接访问https://github.com/zeromicro/go-zero/issues 地址 点击右上角的【New issue】新建issue 填写issue标题和内容 点击【Submit new issue】提交issue 参考文档 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"概念介绍","keywords":"","body":"概念介绍 go-zero 晓黑板golang开源项目,集各种工程实践于一身的web和rpc框架。 goctl 一个旨在为开发人员提高工程效率、降低出错率的辅助工具。 goctl插件 指以goctl为中心的周边二进制资源,能够满足一些个性化的代码生成需求,如路由合并插件goctl-go-compact插件, 生成swagger文档的goctl-swagger插件,生成php调用端的goctl-php插件等。 intellij/vscode插件 在intellij系列产品上配合goctl开发的插件,其将goctl命令行操作使用UI进行替代。 api文件 api文件是指用于定义和描述api服务的文本文件,其以.api后缀结尾,包含api语法描述内容。 goctl环境 goctl环境是使用goctl前的准备环境,包含 golang环境 protoc protoc-gen-go插件 go module | gopath go-zero-demo go-zero-demo里面包含了文档中所有源码的一个大仓库,后续我们在编写演示demo时,我们均在此项目下创建子项目, 因此我们需要提前创建一个大仓库go-zero-demo,我这里把这个仓库放在home目录下。 $ cd ~ $ mkdir go-zero-demo&&cd go-zero-demo $ go mod init go-zero-demo 参考文档 go-zero Goctl 插件中心 工具中心 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"quick-start.html":{"url":"quick-start.html","title":"快速开发","keywords":"","body":"快速开发 本节主要通过对 api/rpc 等服务快速开始来让大家对使用 go-zero 开发的工程有一个宏观概念,更加详细的介绍我们将在后续一一展开。如果您已经参考 准备工作 做好环境及工具的准备,请跟随以下小节开始体验: 单体服务 微服务 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"单体服务","keywords":"","body":"单体服务 前言 由于go-zero集成了web/rpc于一体,社区有部分小伙伴会问我,go-zero的定位是否是一款微服务框架,答案是不止于此, go-zero虽然集众多功能于一身,但你可以将其中任何一个功能独立出来去单独使用,也可以开发单体服务, 不是说每个服务上来就一定要采用微服务的架构的设计,这点大家可以看看作者(kevin)的第四期开源说 ,其中对此有详细的讲解。 创建greet服务 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 查看一下greet服务的目录结构 $ tree greet greet ├── etc │ └── greet-api.yaml ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go 由以上目录结构可以观察到,greet服务虽小,但\"五脏俱全\"。接下来我们就可以在greetlogic.go中编写业务代码了。 编写逻辑 $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req *types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } 启动并访问服务 启动服务 $ cd greet $ go run greet.go -f etc/greet-api.yaml 输出如下,服务启动并侦听在8888端口: Starting server at 0.0.0.0:8888... 访问服务 $ curl -i -X GET http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} 源码 greet源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"micro-service.html":{"url":"micro-service.html","title":"微服务","keywords":"","body":"微服务 在上一篇我们已经演示了怎样快速创建一个单体服务,接下来我们来演示一下如何快速创建微服务, 在本小节中,api部分其实和单体服务的创建逻辑是一样的,只是在单体服务中没有服务间的通讯而已, 且微服务中api服务会多一些rpc调用的配置。 前言 本小节将以一个订单服务调用用户服务来简单演示一下,演示代码仅传递思路,其中有些环节不会一一列举。 情景提要 假设我们在开发一个商城项目,而开发者小明负责用户模块(user)和订单模块(order)的开发,我们姑且将这两个模块拆分成两个微服务① [注意] ①:微服务的拆分也是一门学问,这里我们就不讨论怎么去拆分微服务的细节了。 演示功能目标 订单服务(order)提供一个查询接口 用户服务(user)提供一个方法供订单服务获取用户信息 服务设计分析 根据情景提要我们可以得知,订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯,相信这里我已经对\"rpc服务存在有什么作用?\"已经作了一个比较好的场景描述。 当然,一个服务开发前远不止这点设计分析,我们这里就不详细描述了。从上文得知,我们需要一个 user rpc order api 两个服务来初步实现这个小demo。 创建mall工程 如果你跑了单体的示例,里面也叫 go-zero-demo,你可能需要换一个父目录。 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 创建user rpc服务 创建user rpc服务 $ mkdir -p mall/user/rpc 添加user.proto文件,增加getUser方法 $ vim mall/user/rpc/user.proto 增加如下代码: syntax = \"proto3\"; package user; // protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成 option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // 用户id string id = 1; // 用户名称 string name = 2; // 用户性别 string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } 生成代码 如未安装 protoc,protoc-gen-go,protoc-gen-grpc-go 你可以通过如下指令一键安装: $ goctl env check -i -f 注意: 1、每一个 *.proto文件只允许有一个service error: only one service expected $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. 填充业务逻辑 $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } 创建order api服务 创建 order api服务 # 回到 go-zero-demo/mall 目录 $ mkdir -p order/api && cd order/api 添加api文件 $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } 生成order服务 $ goctl api go -api order.api -dir . Done. 添加user rpc配置 $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } 添加yaml配置 $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc 完善服务依赖 $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 添加order演示逻辑 给 getorderlogic 添加业务逻辑 $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } 启动服务并验证 启动etcd$ etcd 下载依赖# 在 go-zero-demo 目录下 $ go mod tidy 启动user rpc # 在 mall/user/rpc 目录 $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动order api # 在 mall/order/api 目录 $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... 访问order api $ curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\":\"1\",\"name\":\"test order\"} 注意:在演示中的提及的api语法,rpc生成,goctl,goctl环境等怎么使用和安装,快速入门中不作详细概述,我们后续都会有详细的文档进行描述,你也可以点击下文的【猜你想看】快速跳转的对应文档查看。 源码 mall源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 rpc目录 rpc配置 rpc调用方说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"framework-design.html":{"url":"framework-design.html","title":"框架设计","keywords":"","body":"框架设计 本节将从 go-zero 的设计理念,go-zero 服务的最佳实践目录来说明 go-zero 框架的设计,本节将包含以下小节: go-zero设计理念 go-zero特点 api语法介绍 api目录结构 rpc目录结构 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"go-zero设计理念","keywords":"","body":"go-zero设计理念 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"go-zero特点","keywords":"","body":"go-zero特性 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-grammar.html":{"url":"api-grammar.html","title":"api语法介绍","keywords":"","body":"api语法介绍 api示例 /** * api语法示例及语法说明 */ // api语法版本 syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"songmeizi\" date: \"2020-01-08\" desc: \"api语法示例及语法说明\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } api语法结构 syntax语法声明 import语法块 info语法块 type语法块 service语法块 隐藏通道 [!TIP] 在以上语法结构中,各个语法块从语法上来说,按照语法块为单位,可以在.api文件中任意位置声明, 但是为了提高阅读效率,我们建议按照以上顺序进行声明,因为在将来可能会通过严格模式来控制语法块的顺序。 syntax语法声明 syntax是新加入的语法结构,该语法的引入可以解决: 快速针对api版本定位存在问题的语法结构 针对版本做语法解析 防止api语法大版本升级导致前后不能向前兼容 **[!WARNING] 被import的api必须要和main api的syntax版本一致。 语法定义 'syntax'={checkVersion(p)}STRING 语法说明 syntax:固定token,标志一个syntax语法结构的开始 checkVersion:自定义go方法,检测STRING是否为一个合法的版本号,目前检测逻辑为,STRING必须是满足(?m)\"v[1-9][0-9]*\"正则。 STRING:一串英文双引号包裹的字符串,如\"v1\" 一个api语法文件只能有0或者1个syntax语法声明,如果没有syntax,则默认为v1版本 正确语法示例 ✅ eg1:不规范写法 syntax=\"v1\" eg2:规范写法(推荐) syntax = \"v2\" 错误语法示例 ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" import语法块 随着业务规模增大,api中定义的结构体和服务越来越多,所有的语法描述均为一个api文件,这是多么糟糕的一个问题, 其会大大增加了阅读难度和维护难度,import语法块可以帮助我们解决这个问题,通过拆分api文件, 不同的api文件按照一定规则声明,可以降低阅读难度和维护难度。 **[!WARNING] 这里import不像golang那样包含package声明,仅仅是一个文件路径的引入,最终解析后会把所有的声明都汇聚到一个spec.Spec中。 不能import多个相同路径,否则会解析错误。 语法定义 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' 语法说明 import:固定token,标志一个import语法的开始 checkImportValue:自定义go方法,检测STRING是否为一个合法的文件路径,目前检测逻辑为,STRING必须是满足(?m)\"(/?[a-zA-Z0-9_#-])+\\.api\"正则。 STRING:一串英文双引号包裹的字符串,如\"foo.api\" 正确语法示例 ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) 错误语法示例 ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info语法块 info语法块是一个包含了多个键值对的语法体,其作用相当于一个api服务的描述,解析器会将其映射到spec.Spec中, 以备用于翻译成其他语言(golang、java等) 时需要携带的meta元素。如果仅仅是对当前api的一个说明,而不考虑其翻译 时传递到其他语言,则使用简单的多行注释或者java风格的文档注释即可,关于注释说明请参考下文的 隐藏通道。 **[!WARNING] 不能使用重复的key,每个api文件只能有0或者1个info语法块 语法定义 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' 语法说明 info:固定token,标志一个info语法块的开始 checkKeyValue:自定义go方法,检测VALUE是否为一个合法值。 VALUE:key对应的值,可以为单行的除'\\r','\\n','/'后的任意字符,多行请以\"\"包裹,不过强烈建议所有都以\"\"包裹 正确语法示例 ✅ eg1:不规范写法 info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:规范写法(推荐) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) 错误语法示例 ❌ eg1:没有key-value内容 info() eg2:不包含冒号 info( foo value ) eg3:key-value没有换行 info(foo:\"value\") eg4:没有key info( : \"value\" ) eg5:非法的key info( 12: \"value\" ) eg6:移除旧版本多行语法 info( foo: > some text type语法块 在api服务中,我们需要用到一个结构体(类)来作为请求体,响应体的载体,因此我们需要声明一些结构体来完成这件事情, type语法块由golang的type演变而来,当然也保留着一些golang type的特性,沿用golang特性有: 保留了golang内置数据类型bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, 兼容golang struct风格声明 保留golang关键字 **[!WARNING]️ 不支持alias 不支持time.Time数据类型,用int64表示,因为api支持客户端代码生成,并非所有客户端语言都有time.Time对应的类型 结构体名称、字段名称、不能为golang关键字 语法定义 由于其和golang相似,因此不做详细说明,具体语法定义请在 ApiParser.g4 中查看typeSpec定义。 语法说明 参考golang写法 正确语法示例 ✅ eg1:不规范写法 type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // 非导出型字段 bar int `form:\"bar\"` } type( // 非导出型结构体 fooBar struct{ FooBar int `json:\"fooBar\"` } ) eg2:规范写法(推荐) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int `json:\"fooBar\"` } ) 错误语法示例 ❌ eg type Gender int // 不支持 // 非struct token type Foo structure{ CreateTime time.Time // 不支持time.Time,且没有声明 tag } // golang关键字 var type var{} type Foo{ // golang关键字 interface Foo interface // 没有声明 tag } type Foo{ foo int // map key必须要golang内置数据类型,且没有声明 tag m map[Bar]string } [!NOTE] ① tag定义和golang中json tag语法一样,除了json tag外,go-zero还提供了另外一些tag来实现对字段的描述, 详情见下表。 tag表 绑定参数时,以下四个tag只能选择其中一个 tag key 描述 提供方有效范围 示例 json json序列化tag golang request、response json:\"fooo\" path 路由path,如/foo/:id go-zero request path:\"id\" form 标志请求体是一个form(POST方法时)或者一个query(GET方法时/search?name=keyword) go-zero request form:\"name\" header HTTP header,如 Name: value go-zero request header:\"name\" tag修饰符 常见参数校验描述 tag key 描述 提供方 有效范围 示例 optional 定义当前字段为可选参数 go-zero request json:\"name,optional\" options 定义当前字段的枚举值,多个以竖线|隔开 go-zero request json:\"gender,options=male\" default 定义当前字段默认值 go-zero request json:\"gender,default=male\" range 定义当前字段数值范围 go-zero request json:\"age,range=[0:120]\" [!TIP] tag修饰符需要在tag value后以英文逗号,隔开 service语法块 service语法块用于定义api服务,包含服务名称,服务metadata,中间件声明,路由,handler等。 **[!WARNING]️ main api和被import的api服务名称必须一致,不能出现服务名称歧义。 handler名称不能重复 路由(请求方法+请求path)名称不能重复 请求体必须声明为普通(非指针)struct,响应体做了一些向前兼容处理,详请见下文说明 语法定义 serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; 语法说明 serviceSpec:包含了一个可选语法块atServer和serviceApi语法块,其遵循序列模式(编写service必须要按照顺序,否则会解析出错) atServer: 可选语法块,定义key-value结构的server metadata,'@server' 表示这一个server语法块的开始,其可以用于描述serviceApi或者route语法块,其用于描述不同语法块时有一些特殊关键key 需要值得注意,见 atServer关键key描述说明。 serviceApi:包含了1到多个serviceRoute语法块 serviceRoute:按照序列模式包含了atDoc,handler和route atDoc:可选语法块,一个路由的key-value描述,其在解析后会传递到spec.Spec结构体,如果不关心传递到spec.Spec, 推荐用单行注释替代。 handler:是对路由的handler层描述,可以通过atServer指定handler key来指定handler名称, 也可以直接用atHandler语法块来定义handler名称 atHandler:'@handler' 固定token,后接一个遵循正则[_a-zA-Z][a-zA-Z_-]*)的值,用于声明一个handler名称 route:路由,有httpMethod、path、可选request、可选response组成,httpMethod是必须是小写。 body:api请求体语法定义,必须要由()包裹的可选的ID值 replyBody:api响应体语法定义,必须由()包裹的struct、array(向前兼容处理,后续可能会废弃,强烈推荐以struct包裹,不要直接用array作为响应体) kvLit: 同info key-value serviceName: 可以有多个'-'join的ID值 path:api请求路径,必须以'/'或者'/:'开头,切不能以'/'结尾,中间可包含ID或者多个以'-'join的ID字符串 atServer关键key描述说明 修饰service时 key描述示例 jwt声明当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码jwt: Auth group声明当前service或者路由文件分组group: login middleware声明当前service需要开启中间件middleware: AuthMiddleware prefix添加路由分组prefix: api 修饰route时 key描述示例 handler声明一个handler- 正确语法示例 ✅ eg1:不规范写法 @server( jwt: Auth group: foo middleware: AuthMiddleware prefix api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // 非导出型body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// 不推荐数组作为响应体 @handler fooBar post /foo/bar (Foo) returns // 可以省略'returns' } eg2:规范写法(推荐) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } 错误语法示例 ❌ // 不支持空的server语法块 @server( ) // 不支持空的service语法块 service foo-api{ } service foo-api{ @doc kkkk // 简版doc必须用英文双引号引起来 @handler foo post /foo @handler foo // 重复的handler post /bar @handler fooBar post /bar // 重复的路由 // @handler和@doc顺序错误 @handler someHandler @doc \"some doc\" post /some/path // handler缺失 post /some/path/:id @handler reqTest post /foo/req (*Foo) // 不支持除普通结构体外的其他数据类型作为请求体 @handler replyTest post /foo/reply returns (*Foo) // 不支持除普通结构体、数组(向前兼容,后续考虑废弃)外的其他数据类型作为响应体 } 隐藏通道 隐藏通道目前主要为空白符号、换行符号以及注释,这里我们只说注释,因为空白符号和换行符号我们目前拿来也无用。 单行注释 语法定义 '//' ~[\\r\\n]* 语法说明 由语法定义可知道,单行注释必须要以//开头,内容为不能包含换行符 正确语法示例 ✅ // doc // comment 错误语法示例 ❌ // break line comments java风格文档注释 语法定义 '/*' .*? '*/' 语法说明 由语法定义可知道,单行注释必须要以/*开头,*/结尾的任意字符。 正确语法示例 ✅ /** * java-style doc */ 错误语法示例 ❌ /* * java-style doc */ */ Doc&Comment 如果想获取某一个元素的doc或者comment开发人员需要怎么定义? Doc 我们规定上一个语法块(非隐藏通道内容)的行数line+1到当前语法块第一个元素前的所有注释(单行,或者多行)均为doc, 且保留了//、/*、*/原始标记。 Comment 我们规定当前语法块最后一个元素所在行开始的一个注释块(当行,或者多行)为comment 且保留了//、/*、*/原始标记。 语法块Doc和Comment的支持情况 语法块parent语法块DocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ 以下为对应语法块解析后细带doc和comment的写法 // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * 开启jwt鉴权 */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * route doc * post请求 * path为 /foo * 请求体:Foo * 响应体:Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-dir.html":{"url":"api-dir.html","title":"api目录结构","keywords":"","body":"api目录介绍 . ├── etc │ └── greet-api.yaml // 配置文件 ├── go.mod // mod文件 ├── greet.api // api描述文件 ├── greet.go // main函数入口 └── internal ├── config │ └── config.go // 配置声明type ├── handler // 路由及handler转发 │ ├── greethandler.go │ └── routes.go ├── logic // 业务逻辑 │ └── greetlogic.go ├── middleware // 中间件文件 │ └── greetmiddleware.go ├── svc // logic所依赖的资源池 │ └── servicecontext.go └── types // request、response的struct,根据api自动生成,不建议编辑 └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"rpc目录结构","keywords":"","body":"rpc服务目录 proto 文件 greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations! your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. 生成的目录结构 . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] pb.go & _grpc.pb.go 文件所在目录并非固定,该目录有 go_opt & go-grpc_opt 与 proto文件中的 go_package 值共同决定,想要了解grpc代码生成目录逻辑请阅读 Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"project-dev.html":{"url":"project-dev.html","title":"项目开发","keywords":"","body":"项目开发 在前面的章节我们已经从一些概念、背景、快速入门等维度介绍了一下go-zero,看到这里,相信你对go-zero已经有了一些了解, 从这里开始,我们将会从环境准备到服务部署整个流程开始进行讲解,为了保证大家能够彻底弄懂go-zero的开发流程,那就准备你的耐心来接着往下走吧。 在章节中,将包含以下小节: 准备工作 golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 开发规范 命名规范 路由规范 编码规范 开发流程 配置介绍 api配置 rpc配置 业务开发 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 CI/CD 服务部署 日志收集 链路追踪 服务监控 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare.html":{"url":"prepare.html","title":"准备工作","keywords":"","body":"准备工作 在正式进入实际开发之前,我们需要做一些准备工作,比如:Go环境的安装,grpc代码生成使用的工具安装, 必备工具Goctl的安装,Golang环境配置等,本节将包含以下小节: golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"golang-install.html":{"url":"golang-install.html","title":"golang安装","keywords":"","body":"Golang环境安装 前言 开发golang程序,必然少不了对其环境的安装,我们这里选择以1.15.1为例。 官方文档 https://golang.google.cn/doc/install mac OS安装Go 下载并安装Go for Mac 验证安装结果 $ go version go version go1.15.1 darwin/amd64 linux 安装Go 下载Go for Linux 解压压缩包至/usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz 添加/usr/local/go/bin到环境变量 $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile 验证安装结果 $ go version go version go1.15.1 linux/amd64 Windows安装Go 下载并安装Go for Windows 验证安装结果 $ go version go version go1.15.1 windows/amd64 其他 更多操作系统安装见https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gomod-config.html":{"url":"gomod-config.html","title":"go module配置","keywords":"","body":"Go Module设置 Go Module介绍 Modules are how Go manages dependencies.[1] 即Go Module是Golang管理依赖性的方式,像Java中的Maven,Android中的Gradle类似。 MODULE配置 查看GO111MODULE开启情况 $ go env GO111MODULE on 开启GO111MODULE,如果已开启(即执行go env GO111MODULE结果为on)请跳过。 $ go env -w GO111MODULE=\"on\" 设置GOPROXY $ go env -w GOPROXY=https://goproxy.cn 设置GOMODCACHE 查看GOMODCACHE $ go env GOMODCACHE 如果目录不为空或者/dev/null,请跳过。 go env -w GOMODCACHE=$GOPATH/pkg/mod 参考文档 [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-install.html":{"url":"goctl-install.html","title":"goctl安装","keywords":"","body":"Goctl安装 前言 Goctl在go-zero项目开发着有着很大的作用,其可以有效的帮助开发者大大提高开发效率,减少代码的出错率,缩短业务开发的工作量,更多的Goctl的介绍请阅读Goctl介绍, 在这里我们强烈推荐大家安装,因为后续演示例子中我们大部分都会以goctl进行演示。 安装(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 环境变量检测 go get下载编译后的二进制文件位于$GOPATH/bin目录下,要确保$GOPATH/bin已经添加到环境变量。 $ sudo vim /etc/paths 在最后一行添加如下内容 $GOPATH/bin [!TIP] $GOPATH为你本机上的文件地址 安装结果验证 $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] windows用户添加环境变量请自行google Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go安装","keywords":"","body":"protoc & protoc-gen-go安装 前言 protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。 mac OS方式一:goctl一键安装 $ goctl env check -i -f --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! 方式二: 源文件安装 protoc安装 进入protobuf release 页面,选择适合自己操作系统的压缩包文件 解压protoc-x.x.x-osx-x86_64.zip并进入protoc-x.x.x-osx-x86_64 $ cd protoc-x.x.x-osx-x86_64/bin 将启动的protoc二进制文件移动到被添加到环境变量的任意path下,如$GOPATH/bin,这里不建议直接将其和系统的以下path放在一起。 $ mv protoc $GOPATH/bin [!TIP] $GOPATH为你本机的实际文件夹地址 验证安装结果 $ protoc --version libprotoc x.x.x protoc-gen-go/protoc-gen-go-grpc 安装 下载安装protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest [!WARNING] protoc-gen-go安装失败请阅读常见错误处理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare-other.html":{"url":"prepare-other.html","title":"其他","keywords":"","body":"其他 在之前我们已经对Go环境、Go Module配置、Goctl、protoc & protoc-gen-go安装准备就绪,这些是开发人员在开发阶段必须要准备的环境,而接下来的环境你可以选择性的安装, 因为这些环境一般存在于服务器(安装工作运维会替你完成),但是为了后续演示流程能够完整走下去,我建议大家在本地也安装一下,因为我们的演示环境大部分会以本地为主。 以下仅给出了需要的准备工作,不以文档篇幅作详细介绍了。 其他环境 etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-specification.html":{"url":"dev-specification.html","title":"开发规范","keywords":"","body":"开发规范 在实际业务开发中,除了要提高业务开发效率,缩短业务开发周期,保证线上业务高性能,高可用的指标外,好的编程习惯也是一个开发人员基本素养之一,在本章节, 我们将介绍一下go-zero中的编码规范,本章节为可选章节,内容仅供交流与参考,本章节将从以下小节进行说明: 命名规范 路由规范 编码规范 开发三原则 Clarity(清晰) 作者引用了Hal Abelson and Gerald Sussman的一句话: Programs must be written for people to read, and only incidentally for machines to execute 程序是什么,程序必须是为了开发人员阅读而编写的,只是偶尔给机器去执行,99%的时间程序代码面向的是开发人员,而只有1%的时间可能是机器在执行,这里比例不是重点,从中我们可以看出,清晰的代码是多么的重要,因为所有程序,不仅是Go语言,都是由开发人员编写,供其他人阅读和维护。 Simplicity(简单) Simplicity is prerequisite for reliability Edsger W. Dijkstra认为:可靠的前提条件就是简单,我们在实际开发中都遇到过,这段代码在写什么,想要完成什么事情,开发人员不理解这段代码,因此也不知道如何去维护,这就带来了复杂性,程序越是复杂就越难维护,越难维护就会是程序变得越来越复杂,因此,遇到程序变复杂时首先应该想到的是——重构,重构会重新设计程序,让程序变得简单。 Productivity(生产力) 在go-zero团队中,一直在强调这个话题,开发人员成产力的多少,并不是你写了多少行代码,完成了多少个模块开发,而是我们需要利用各种有效的途径来利用有限的时间完成开发效率最大化,而Goctl的诞生正是为了提高生产力, 因此这个开发原则我是非常认同的。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"naming-spec.html":{"url":"naming-spec.html","title":"命名规范","keywords":"","body":"命名规范 在任何语言开发中,都有其语言领域的一些命名规范,好的命名可以: 降低代码阅读成本 降低维护难度 降低代码复杂度 规范建议 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。 命名准则 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好。 变量命名应尽量描述其内容,而不是类型 常量命名应尽量描述其值,而不是如何使用这个值 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值 method、interface、type、package推荐使用单词命名 package名称也是命名的一部分,请尽量将其利用起来 使用一致的命名风格 文件命名规范 全部小写 除unit test外避免下划线(_) 文件名称不宜过长 变量命名规范参考 首字母小写 驼峰命名 见名知义,避免拼音替代英文 不建议包含下划线(_) 不建议包含数字 适用范围 局部变量 函数出参、入参 函数、常量命名规范 驼峰式命名 可exported的必须首字母大写 不可exported的必须首字母小写 避免全部大写与下划线(_)组合 [!TIP] 如果是go-zero代码贡献,则必须严格遵循此命名规范 参考文档 Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"路由规范","keywords":"","body":"路由规范 推荐脊柱式命名 小写单词、横杠(-)组合 见名知义 /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"coding-spec.html":{"url":"coding-spec.html","title":"编码规范","keywords":"","body":"编码规范 import 单行import不建议用圆括号包裹 按照官方包,NEW LINE,当前工程包,NEW LINE,第三方依赖包顺序引入 import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) 函数返回 对象避免非指针返回 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则 错误处理 有error必须处理,如果不能处理就必须抛出。 避免下划线(_)接收error 函数体编码 建议一个block结束空一行,如if、for等 func main (){ if x==1{ // do something } fmt.println(\"xxx\") } return前空一行 func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-flow.html":{"url":"dev-flow.html","title":"开发流程","keywords":"","body":"开发流程 这里的开发流程和我们实际业务开发流程不是一个概念,这里的定义局限于go-zero的使用,即代码层面的开发细节。 开发流程 goctl环境准备[1] 数据库设计 业务开发 新建工程 创建服务目录 创建服务类型(api/rpc/rmq/job/script) 编写api、proto文件 代码生成 生成数据库访问层代码model 配置config,yaml变更 资源依赖填充(ServiceContext) 添加中间件 业务代码填充 错误处理 [!TIP] [1] goctl环境 开发工具 Visual Studio Code Goland(推荐) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"config-introduction.html":{"url":"config-introduction.html","title":"配置介绍","keywords":"","body":"配置介绍 在正式使用go-zero之前,让我们先来了解一下go-zero中不同服务类型的配置定义,看看配置中每个字段分别有什么作用,本节将包含以下小节: api配置 rpc配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-config.html":{"url":"api-config.html","title":"api配置","keywords":"","body":"api配置 api配置控制着api服务中的各种功能,包含但不限于服务监听地址,端口,环境配置,日志配置等,下面我们从一个简单的配置来看一下api中常用配置分别有什么作用。 配置说明 通过yaml配置我们会发现,有很多参数我们并没有与config对齐,这是因为config定义中,有很多都是带optional或者default 标签的,对于optional可选项,你可以根据自己需求判断是否需要设置,对于default标签,如果你觉得默认值就已经够了,可以不用设置, 一般default中的值基本不用修改,可以认为是最佳实践值。 Config type Config struct{ rest.RestConf // rest api配置 Auth struct { // jwt鉴权配置 AccessSecret string // jwt密钥 AccessExpire int64 // 有效期,单位:秒 } Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库 DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可 } CacheRedis cache.CacheConf // redis缓存 UserRpc zrpc.RpcClientConf // rpc client配置 } rest.RestConf api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制,对其展开我们可以看到: service.ServiceConf // service配置 Host string `json:\",default=0.0.0.0\"` // http监听ip,默认0.0.0.0 Port int // http监听端口,必填 CertFile string `json:\",optional\"` // https证书文件,可选 KeyFile string `json:\",optional\"` // https私钥文件,可选 Verbose bool `json:\",optional\"` // 是否打印详细http请求日志 MaxConns int `json:\",default=10000\"` // http同时可接受最大请求数(限流数),默认10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http可接受请求的最大ContentLength,默认1048576,被设置值必须在0到8388608之间 // milliseconds Timeout int64 `json:\",default=3000\"` // 超时时长控制,单位:毫秒,默认3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // cpu降载阈值,默认900,可允许设置范围0到1000 Signature SignatureConf `json:\",optional\"` // 签名配置 service.ServiceConf type ServiceConf struct { Name string // 服务名称 Log logx.LogConf // 日志配置 Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // 服务环境,dev-开发环境,test-测试环境,pre-预发环境,pro-正式环境 MetricsUrl string `json:\",optional\"` // 指标上报接口地址,该地址需要支持post json即可 Prometheus prometheus.Config `json:\",optional\"` // prometheus配置 } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // 服务名称 Mode string `json:\",default=console,options=console|file|volume\"` // 日志模式,console-输出到console,file-输出到当前服务器(容器)文件,,volume-输出docker挂载文件内 Path string `json:\",default=logs\"` // 日志存储路径 Level string `json:\",default=info,options=info|error|severe\"` // 日志级别 Compress bool `json:\",optional\"` // 是否开启gzip压缩 KeepDays int `json:\",optional\"` // 日志保留天数 StackCooldownMillis int `json:\",default=100\"` // 日志write间隔 } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus 监听host Port int `json:\",default=9101\"` // prometheus 监听端口 Path string `json:\",default=/metrics\"` // 上报地址 } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // 是否Strict模式,如果是则PrivateKeys必填 Expiry time.Duration `json:\",default=1h\"` // 有效期,默认1小时 PrivateKeys []PrivateKeyConf // 签名密钥相关配置 } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // 指纹配置 KeyFile string // 密钥配置 } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // 权重 } redis.RedisConf RedisConf struct { Host string // redis地址 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-config.html":{"url":"rpc-config.html","title":"rpc配置","keywords":"","body":"rpc配置 rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等,下面我们以一个常见的rpc服务配置来进行说明。 配置说明 Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // redis缓存配置,详情见api配置说明,这里不赘述 Mysql struct { // mysql数据库访问配置,详情见api配置说明,这里不赘述 DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // 服务配置,详情见api配置说明,这里不赘述 ListenOn string // rpc监听地址和端口,如:127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd相关配置 Auth bool `json:\",optional\"` // 是否开启Auth,如果是则Redis为必填 Redis redis.RedisKeyConf `json:\",optional\"` // Auth验证 StrictControl bool `json:\",optional\"` // 是否Strict模式,如果是则遇到错误是Auth失败,否则可以认为成功 // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // 超时控制,单位:毫秒 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` cpu降载阈值,默认900,可允许设置范围0到1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host数组 Key string // rpc注册key } redis.RedisKeyConf RedisConf struct { Host string // redis 主机 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // 验证key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-dev.html":{"url":"business-dev.html","title":"业务开发","keywords":"","body":"业务开发 本章节我们用一个简单的示例去演示一下go-zero中的一些基本功能。本节将包含以下小节: 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 演示工程下载 在正式进入后续文档叙述前,可以先留意一下这里的源码,后续我们会基于这份源码进行功能的递进式演示, 而不是完全从0开始,如果你从快速入门章节过来,这份源码结构对你来说不是问题。 点击这里下载演示工程基础源码 演示工程说明 场景 程序员小明需要借阅一本《西游记》,在没有线上图书管理系统的时候,他每天都要去图书馆前台咨询图书馆管理员, 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,明天再来看看吧。 过了一天,小明又来到图书馆,问: 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,你过两天再来看看吧。 就这样经过多次反复,小明也是徒劳无功,浪费大量时间在来回的路上,于是终于忍受不了落后的图书管理系统, 他决定自己亲手做一个图书查阅系统。 预期实现目标 用户登录 依靠现有学生系统数据进行登录 图书检索 根据图书关键字搜索图书,查询图书剩余数量。 系统分析 服务拆分 user api 提供用户登录协议 rpc 供search服务访问用户数据 search api 提供图书查询协议 [!TIP] 这个微小的图书借阅查询系统虽然小,从实际来讲不太符合业务场景,但是仅上面两个功能,已经满足我们对go-zero api/rpc的场景演示了, 后续为了满足更丰富的go-zero功能演示,会在文档中进行业务插入即相关功能描述。这里仅用一个场景进行引入。 注意:user中的sql语句请自行创建到db中去,更多准备工作见准备工作 添加一些预设的用户数据到数据库,便于后面使用,为了篇幅,演示工程不对插入数据这种操作做详细演示。 参考预设数据 INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-design.html":{"url":"service-design.html","title":"目录拆分","keywords":"","body":"目录拆分 目录拆分是指配合go-zero的最佳实践的目录拆分,这和微服务拆分有着关联,在团队内部最佳实践中, 我们按照业务横向拆分,将一个系统拆分成多个子系统,每个子系统应拥有独立的持久化存储,缓存系统。 如一个商城系统需要有用户系统(user),商品管理系统(product),订单系统(order),购物车系统(cart),结算中心系统(pay),售后系统(afterSale)等组成。 系统结构分析 在上文提到的商城系统中,每个系统在对外(http)提供服务的同时,也会提供数据给其他子系统进行数据访问的接口(rpc),因此每个子系统可以拆分成一个服务,而且对外提供了两种访问该系统的方式api和rpc,因此, 以上系统按照目录结构来拆分有如下结构: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc调用链建议 在设计系统时,尽量做到服务之间调用链是单向的,而非循环调用,例如:order服务调用了user服务,而user服务反过来也会调用order的服务, 当其中一个服务启动故障,就会相互影响,进入死循环,你order认为是user服务故障导致的,而user认为是order服务导致的,如果有大量服务存在相互调用链, 则需要考虑服务拆分是否合理。 常见服务类型的目录结构 在上述服务中,仅列举了api/rpc服务,除此之外,一个服务下还可能有其他更多服务类型,如rmq(消息处理系统),cron(定时任务系统),script(脚本)等, 因此一个服务下可能包含以下目录结构: user ├── api // http访问服务,业务需求实现 ├── cronjob // 定时任务,定时数据更新业务 ├── rmq // 消息处理系统:mq和dq,处理一些高并发和延时消息业务 ├── rpc // rpc服务,给其他子系统提供基础数据访问 └── script // 脚本,处理一些临时运营需求,临时数据修复 完整工程目录结构示例 mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script 猜你想看 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"model-gen.html":{"url":"model-gen.html","title":"model生成","keywords":"","body":"model生成 首先,下载好演示工程 后,我们以user的model来进行代码生成演示。 前言 model是服务访问持久化数据层的桥梁,业务的持久化数据常存在于mysql,mongo等数据库中,我们都知道,对于一个数据库的操作莫过于CURD, 而这些工作也会占用一部分时间来进行开发,我曾经在编写一个业务时写了40个model文件,根据不同业务需求的复杂性,平均每个model文件差不多需要 10分钟,对于40个文件来说,400分钟的工作时间,差不多一天的工作量,而goctl工具可以在10秒钟来完成这400分钟的工作。 准备工作 进入演示工程book,找到user/model下的user.sql文件,将其在你自己的数据库中执行建表。 代码生成(带缓存) 方式一(ddl) 进入service/user/model目录,执行命令 $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. 方式二(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource为数据库连接地址 方式三(intellij 插件) 在Goland中,右键user.sql,依次进入并点击New->Go Zero->Model Code即可生成,或者打开user.sql文件, 进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Mode Code即可 [!TIP] intellij插件生成需要安装goctl插件,详情见intellij插件 验证生成的model文件 查看tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go 更多 对于持久化数据,如果需要更灵活的数据库能力,包括事务能力,可以参考 Mysql 如果需要分布式事务的能力,可以参考 分布式事务支持 猜你想看 model命令及其原理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-coding.html":{"url":"api-coding.html","title":"api文件编写","keywords":"","body":"api文件编写 编写user.api文件 $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } 生成api服务 方式一 $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. 方式二 在 user.api 文件右键,依次点击进入 New->Go Zero->Api Code ,进入目标目录选择,即api源码的目标存放目录,默认为user.api所在目录,选择好目录后点击OK即可。 方式三 打开user.api,进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Api Code,同样进入目录选择弹窗,选择好目录后点击OK即可。 猜你想看 api语法 goctl api命令 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-coding.html":{"url":"business-coding.html","title":"业务编码","keywords":"","body":"业务编码 前面一节,我们已经根据初步需求编写了user.api来描述user服务对外提供哪些服务访问,在本节我们接着前面的步伐, 通过业务编码来讲述go-zero怎么在实际业务中使用。 添加Mysql配置 $ vim service/user/api/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/core/stores/cache\" ) type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } 完善yaml配置 $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 更多配置信息,请参考api配置介绍 完善服务依赖 $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } 填充登录逻辑 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"用户密码不正确\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] 上述代码中 [start]-[end]的代码实现见jwt鉴权章节 猜你想看 api语法 goctl api命令 api目录结构介绍 jwt鉴权 api配置介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"jwt.html":{"url":"jwt.html","title":"jwt鉴权","keywords":"","body":"jwt鉴权 概述 JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。 什么时候应该使用JWT 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。 信息交换:JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。 为什么要使用JSON Web令牌 由于JSON不如XML冗长,因此在编码时JSON的大小也较小,从而使JWT比SAML更为紧凑。这使得JWT是在HTML和HTTP环境中传递的不错的选择。 在安全方面,只能使用HMAC算法由共享机密对SWT进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公用/专用密钥对进行签名。与签署JSON的简单性相比,使用XML Digital Signature签署XML而不引入模糊的安全漏洞是非常困难的。 JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象的映射。与SAML断言相比,这使使用JWT更加容易。 关于用法,JWT是在Internet规模上使用的。这突显了在多个平台(尤其是移动平台)上对JSON Web令牌进行客户端处理的简便性。 [!TIP] 以上内容全部来自jwt官网介绍 go-zero中怎么使用jwt jwt鉴权一般在api层使用,我们这次演示工程中分别在user api登录时生成jwt token,在search api查询图书时验证用户jwt token两步来实现。 user api生成jwt token 接着业务编码章节的内容,我们完善上一节遗留的getJwtToken方法,即生成jwt token逻辑 添加配置定义和yaml配置项 $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:生成jwt token的密钥,最简单的方式可以使用一个uuid值。 $AccessExpire:jwt token有效期,单位:秒 更多配置信息,请参考api配置介绍 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search api使用jwt token鉴权 编写search.api文件 $ vim service/search/api/search.api type ( SearchReq { // 图书名称 Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth:开启jwt鉴权 如果路由需要jwt鉴权,则需要在service上方声明此语法标志,如上文中的/search/do 不需要jwt鉴权的路由就无需声明,如上文中/search/ping 更多语法请阅读api语法介绍 生成代码 前面已经描述过有三种方式去生成代码,这里就不赘述了。 添加yaml配置项 $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 这里修改一下端口,避免和user api端口8888冲突 验证 jwt token 启动user api服务,登录 $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'Content-Type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' 如果是在Windows的CMD里运行,命令格式如下: curl -i -X POST http://127.0.0.1:8888/user/login -H \"Content-Type: application/json\" -d \"{ \\\"username\\\":\\\"666\\\", \\\"password\\\":\\\"123456\\\" }\" 访问结果: HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"小明\",\"gender\":\"男\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} 启动search api服务,调用/search/do验证jwt鉴权是否通过 $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... 我们先不传jwt token,看看结果 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 很明显,jwt鉴权失败了,返回401的statusCode,接下来我们带一下jwt token(即用户登录返回的accessToken) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] 服务启动错误,请查看常见错误处理 至此,jwt从生成到使用就演示完成了,jwt token的鉴权是go-zero内部已经封装了,你只需在api文件中定义服务时简单的声明一下即可。 获取jwt token中携带的信息 go-zero从jwt token解析后会将用户生成token时传入的kv原封不动的放在http.Request的Context中,因此我们可以通过Context就可以拿到你想要的值 $ vim /service/search/api/internal/logic/searchlogic.go 添加一个log来输出从jwt解析出来的userId。 func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } 运行结果 {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} 猜你想看 jwt介绍 api配置介绍 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"middleware.html":{"url":"middleware.html","title":"中间件使用","keywords":"","body":"中间件使用 在上一节,我们演示了怎么使用jwt鉴权,相信你已经掌握了对jwt的基本使用,本节我们来看一下api服务中间件怎么使用。 中间件分类 在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,其和jwt类似,没有放在jwt:xxx下的路由不会使用中间件功能, 而全局中间件的服务范围则是整个服务。 中间件使用 这里以search服务为例来演示中间件的使用 路由中间件 重新编写search.api文件,添加middleware声明 $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // 路由中间件声明 ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } 重新生成api代码 $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. 生成完后会在internal目录下多一个middleware的目录,这里即中间件文件,后续中间件的实现逻辑也在这里编写。 完善资源依赖ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } 编写中间件逻辑 这里仅添加一行日志,内容example middle,如果服务运行输出example middle则代表中间件使用起来了。 $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } 启动服务验证 {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} 全局中间件 通过rest.Server提供的Use方法即可 func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // 全局中间件 server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} 在中间件里调用其它服务 通过闭包的方式把其它服务传递给中间件,示例如下: // 模拟的其它服务 type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // 常规中间件 func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // 调用其它服务的中间件 func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } 完整代码参考:https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-call.html":{"url":"rpc-call.html","title":"rpc服务编写与调用","keywords":"","body":"rpc编写与调用 在一个大的系统中,多个子系统(服务)间必然存在数据传递,有数据传递就需要通信方式,你可以选择最简单的http进行通信,也可以选择rpc服务进行通信, 在go-zero,我们使用zrpc来进行服务间的通信,zrpc是基于grpc。 场景 在前面我们完善了对用户进行登录,用户查询图书等接口协议,但是用户在查询图书时没有做任何用户校验,如果当前用户是一个不存在的用户则我们不允许其查阅图书信息, 从上文信息我们可以得知,需要user服务提供一个方法来获取用户信息供search服务使用,因此我们就需要创建一个user rpc服务,并提供一个getUser方法。 rpc服务编写 编译proto文件 $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } 生成rpc服务代码$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] 如果安装的 protoc-gen-go 版大于1.4.0, proto文件建议加上go_package 添加配置及完善yaml配置项 $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 $etcdHost: etcd连接地址,格式:ip:port,如: 127.0.0.1:2379 更多配置信息,请参考rpc配置介绍 添加资源依赖 $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } 添加rpc逻辑 $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } 使用rpc 接下来我们在search服务中调用user rpc 添加UserRpc配置及yaml配置项 $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 $etcdHost: etcd连接地址 etcd中的Key必须要和user rpc服务配置中Key一致 添加依赖 $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 补充逻辑 $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // 使用user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } 启动并验证服务 启动etcd、redis、mysql 启动user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml 验证服务 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"西游记\",\"count\":100} 猜你想看 rpc配置 rpc服务目录 goctl rpc命令 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error-handle.html":{"url":"error-handle.html","title":"错误处理","keywords":"","body":"错误处理 错误的处理是一个服务必不可缺的环节。在平时的业务开发中,我们可以认为http状态码不为2xx系列的,都可以认为是http请求错误, 并伴随响应的错误信息,但这些错误信息都是以plain text形式返回的。除此之外,我在业务中还会定义一些业务性错误,常用做法都是通过 code、msg 两个字段来进行业务处理结果描述,并且希望能够以json响应体来进行响应。 业务错误响应格式 业务处理正常 { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } 业务处理异常 { \"code\": 10001, \"msg\": \"参数错误\" } user api之login 在之前,我们在登录逻辑中处理用户名不存在时,直接返回来一个error。我们来登录并传递一个不存在的用户名看看效果。 curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 用户名不存在 接下来我们将其以json格式进行返回 自定义错误 首先在common中添加一个baseerror.go文件,并填入代码 $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } 将登录逻辑中错误用CodeError自定义错误替换 if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"用户密码不正确\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil 开启自定义错误 $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // 自定义错误 httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } 重启服务验证 $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"用户名不存在\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD 在软件工程中,CI/CD或CICD通常指的是持续集成和持续交付或持续部署的组合实践。 ——引自维基百科 CI可以做什么? 现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为“合并日”),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。 持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或“主干”中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。 ——引自《CI/CD是什么?如何理解持续集成、持续交付和持续部署》 从概念上来看,CI/CD包含部署过程,我们这里将部署(CD)单独放在一节服务部署, 本节就以gitlab来做简单的CI(Run Unit Test)演示。 gitlab CI Gitlab CI/CD是Gitlab内置的软件开发工具,提供 持续集成(CI) 持续交付(CD) 持续部署(CD) 准备工作 gitlab安装 git安装 gitlab runner安装 开启gitlab CI 上传代码 在gitlab新建一个仓库go-zero-demo 将本地代码上传到go-zero-demo仓库 在项目根目录下创建.gitlab-ci.yaml文件,通过此文件可以创建一个pipeline,其会在代码仓库中有内容变更时运行,pipeline由一个或多个按照顺序运行, 每个阶段可以包含一个或者多个并行运行的job。 添加CI内容(仅供参考) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] 以上CI为简单的演示,详细的gitlab CI请参考gitlab官方文档进行更丰富的CI集成。 参考文档 CI/CD 维基百科 CI/CD是什么?如何理解持续集成、持续交付和持续部署 Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-deployment.html":{"url":"service-deployment.html","title":"服务部署","keywords":"","body":"服务部署 本节通过jenkins来进行简单的服务部署到k8s演示。 准备工作 k8s集群安装 gitlab环境安装 jenkins环境安装 redis&mysql&nginx&etcd安装 goctl安装 [!TIP] goctl确保k8s每个node节点上都有 以上环境安装请自行google,这里不做篇幅介绍。 服务部署 1、gitlab代码仓库相关准备 1.1、添加SSH Key 进入gitlab,点击用户中心,找到Settings,在左侧找到SSH Keystab 1、在jenkins所在机器上查看公钥 $ cat ~/.ssh/id_rsa.pub 2、如果没有,则需要生成,如果存在,请跳转到第3步 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 完成生成后,重复第一步操作 3、将公钥添加到gitlab中 1.2、上传代码到gitlab仓库 新建工程go-zero-demo并上传代码,这里不做细节描述。 2、jenkins 2.1、添加凭据 查看jenkins所在机器的私钥,与前面gitlab公钥对应 $ cat id_rsa 进入jenkins,依次点击Manage Jenkins-> Manage Credentials 进入全局凭据页面,添加凭据,Username是一个标识,后面添加pipeline你知道这个标识是代表gitlab的凭据就行,Private Key`即上面获取的私钥 2.2、 添加全局变量 进入Manage Jenkins->Configure System,滑动到全局属性条目,添加docker私有仓库相关信息,如图为docker用户名、docker用户密码、docker私有仓库地址 [!TIP] docker_user 修改为你的docker用户名 docker_pass 修改为你的docker用户密码 docker_server 修改为你的docker服务器地址 这里我使用的私有仓库,如果没有云厂商提供的私有仓库使用,可以自行搭建一个私有仓库,这里就不赘述了,大家自行google。 2.3、配置git 进入Manage Jenkins->Global Tool Configureation,找到Git条目,填写jenkins所在机器git可执行文件所在path,如果没有的话,需要在jenkins插件管理中下载Git插件。 2.4、 添加一个Pipeline pipeline用于构建项目,从gitlab拉取代码->生成Dockerfile->部署到k8s均在这个步骤去做,这里是演示环境,为了保证部署流程顺利, 需要将jenkins安装在和k8s集群的其中过一个节点所在机器上,我这里安装在master上的。 获取凭据id 进入凭据页面,找到Username为gitlab的凭据id 进入jenkins首页,点击新建Item,名称为user 查看项目git地址 添加服务类型Choice Parameter,在General中勾选This project is parameterized,点击添加参数选择Choice Parameter,按照图中添加选择的值常量(api、rpc)及接收值的变量(type),后续在Pipeline script中会用到。 配置user,在user配置页面,向下滑动找到Pipeline script,填写脚本内容 pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: '选择需要构建的分支' } stages { stage('服务信息') { steps { sh 'echo 分支:$branch' sh 'echo 构建服务类型:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('获取commit_id') { steps { echo '获取commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl版本检测') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo 镜像名称:${image}' sh 'docker build -t ${image} .' } } stage('上传到镜像仓库') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('部署到k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId}要替换为你的具体凭据值,即【添加凭据】模块中的一串字符串,${gitUrl}需要替换为你代码的git仓库地址,其他的${xxx}形式的变量无需修改,保持原样即可。 port.sh参考内容如下 case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac 其中dockerlogin.sh内容 #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker登录用户名 $docker-pass: docker登录用户密码 $docker-server: docker私有地址 查看pipeline 查看k8s服务 猜你想看 goctl安装 k8s介绍 docker介绍 jenkins安装 jenkins pipeline nginx文档介绍 etcd文档说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"log-collection.html":{"url":"log-collection.html","title":"日志收集","keywords":"","body":"日志收集 为了保证业务稳定运行,预测服务不健康风险,日志的收集可以帮助我们很好的观察当前服务的健康状况, 在传统业务开发中,机器部署还不是很多时,我们一般都是直接登录服务器进行日志查看、调试,但随着业务的增大,服务的不断拆分, 服务的维护成本也会随之变得越来越复杂,在分布式系统中,服务器机子增多,服务分布在不同的服务器上,当遇到问题时, 我们不能使用传统做法,登录到服务器进行日志排查和调试,这个复杂度可想而知。 [!TIP] 如果是一个简单的单体服务系统或者服务过于小不建议直接使用,否则会适得其反。 准备工作 kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash filebeat配置 $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # 开启json解析 json.keys_under_root: true json.add_error_key: true # 日志文件路径 paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # 定义kafka topic field fields: log_topic: log-collection # 输出到kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx为filebeat.yaml所在路径 go-stash配置 新建config.yaml文件 添加配置内容 $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC 启动服务(按顺序启动) 启动kafka 启动elasticsearch 启动kibana 启动go-stash 启动filebeat 启动order-api服务及其依赖服务(go-zero-demo工程中的order-api服务) 访问kibana 进入127.0.0.1:5601 [!TIP] 这里仅演示收集服务中通过logx产生的日志,nginx中日志收集同理。 参考文档 kafka elasticsearch kibana filebeat go-stash filebeat配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"trace.html":{"url":"trace.html","title":"链路追踪","keywords":"","body":"go-zero链路追踪 序言 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http 。而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪。 追踪的方法就是在一个请求开始时生成一个自己的 spanID ,随着整个请求链路传下去。我们则通过这个 spanID 查看整个链路的情况和性能问题。 下面来看看 go-zero 的链路实现。 代码结构 spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 概念 SpanContext 在介绍 span 之前,先引入 context 。SpanContext 保存了分布式追踪的上下文信息,包括 Trace id,Span id 以及其它需要传递到下游的内容。OpenTracing 的实现需要将 SpanContext 通过某种协议 进行传递,以将不同进程中的 Span 关联到同一个 Trace 上。对于 HTTP 请求来说,SpanContext 一般是采用 HTTP header 进行传递的。 下面是 go-zero 默认实现的 spanContext type spanContext struct { traceId string // TraceID 表示tracer的全局唯一ID spanId string // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一 } 同时开发者也可以实现 SpanContext 提供的接口方法,实现自己的上下文信息传递: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // 自定义操作TraceId,SpanId } Span 一个 REST 调用或者数据库操作等,都可以作为一个 span 。 span 是分布式追踪的最小跟踪单位,一个 Trace 由多段 Span 组成。追踪信息包含如下信息: type Span struct { ctx spanContext // 传递的上下文 serviceName string // 服务名 operationName string // 操作 startTime time.Time // 开始时间戳 flag string // 标记开启trace是 server 还是 client children int // 本 span fork出来的 childsnums } 从 span 的定义结构来看:在微服务中, 这就是一个完整的子调用过程,有调用开始 startTime ,有标记自己唯一属性的上下文结构 spanContext 以及 fork 的子节点数。 实例应用 在 go-zero 中http,rpc中已经作为内置中间件集成。我们以 http ,rpc 中,看看 tracing 是怎么使用的: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } 将 header -> carrier,获取 header 中的traceId等信息 开启一个新的 span,并把「traceId,spanId」封装在context中 从上述的 carrier「也就是header」获取traceId,spanId 看header中是否设置 如果没有设置,则随机生成返回 从 request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回 从上述的 context,拷贝一份到当前的 request 这样就实现了 span 的信息随着 request 传递到下游服务。 RPC 在 rpc 中存在 client, server ,所以从 tracing 上也有 clientTracing, serverTracing 。 serveTracing 的逻辑基本与 http 的一致,来看看 clientTracing 是怎么使用的? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** 将 pair 中的data以map的形式加入 ctx ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } 获取上游带下来的 span 上下文信息 从获取的 span 中创建新的 ctx,span「继承父span的traceId」 将生成 span 的data加入ctx,传递到下一个中间件,流至下游 总结 go-zero 通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。开发者可以通过 ELK 工具追踪 traceID ,看到整个调用链。 同时 go-zero 并没有提供整套 trace 链路方案,开发者可以封装 go-zero 已有的 span 结构,做自己的上报系统,接入 jaeger, zipkin 等链路追踪工具。 参考 go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-monitor.html":{"url":"service-monitor.html","title":"服务监控","keywords":"","body":"服务监控 在微服务治理中,服务监控也是非常重要的一个环节,监控一个服务是否正常工作,需要从多维度进行,如: mysql指标 mongo指标 redis指标 请求日志 服务指标统计 服务健康检测 ... 监控的工作非常大,本节仅以其中的服务指标监控作为例子进行说明。 基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 $ prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl goctl是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有: api服务生成 rpc服务生成 model代码生成 模板管理 本节将包含以下内容: 自动补全设置 命令大全 api命令 rpc命令 model命令 plugin命令 其他命令 goctl 读音 很多人会把 goctl 读作 go-C-T-L,这种是错误的念法,应参照 go control 读做 ɡō kənˈtrōl。 查看版本信息 $ goctl -v 如果安装了goctl则会输出以下格式的文本信息: goctl version ${version} ${os}/${arch} 例如输出: goctl version 1.1.5 darwin/amd64 版本号说明 version:goctl 版本号 os:当前操作系统名称 arch: 当前系统架构名称 安装 goctl 方式一(go get) # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 通过此命令可以将goctl工具安装到 $GOPATH/bin 目录下 方式二 (fork and build) 从 go-zero代码仓库 git@github.com:zeromicro/go-zero.git 拉取一份源码,进入 tools/goctl/目录下编译一下 goctl 文件,然后将其添加到环境变量中。 安装完成后执行goctl -v,如果输出版本信息则代表安装成功,例如: $ goctl -v goctl version 1.1.4 darwin/amd64 常见问题 command not found: goctl 请确保goctl已经安装,或者goctl是否已经正确添加到当前shell的环境变量中。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"自动补全设置","keywords":"","body":"goctl自动补全 goctl 自动补全仅支持 unix-like 操作系统 用法 $ goctl completion -h NAME: goctl completion - generation completion script, it only works for unix-like OS USAGE: goctl completion [command options] [arguments...] OPTIONS: --name value, -n value the filename of auto complete script, default is [goctl_autocomplete] 生成自动补全文件 $ goctl completion generation auto completion success! executes the following script to setting shell: echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell 配置 zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc 演示效果 使用 tab 键出现自动补全提示 $ goctl api -- generate api related files bug -- report a bug completion -- generation completion script, it only works for unix-like OS docker -- generate Dockerfile help h -- Shows a list of commands or help for one command kube -- generate kubernetes files migrate -- migrate from tal-tech to zeromicro model -- generate model code rpc -- generate rpc code template -- template operation upgrade -- upgrade goctl to latest version Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"命令大全","keywords":"","body":"goctl命令大全 goctl bug (报告一个错误) upgrade (将goctl升级到最新版本) env (检查或编辑goctl环境) --write, -w: 编辑goctl环境 check (检测goctl环境和依赖性工具) --force, -f: 默许安装不存在的依赖项 --install, -i: 如果没有找到,就安装依赖工具 migrate (从tal-tech迁移到zeromicro) --verbose, -v: verbose可以实现额外的日志记录 --version: 要迁移的github.com/zeromicro/go-zero的目标版本。 api (生成api相关文件) --branch:远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 -o:输出api文件 new (快速创建api服务) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (格式化api文件) --declare:用于跳过检查已经声明的api类型 --dir: 格式目标目录 --iu: 忽略更新 --stdin:使用stdin输入api文件内容,按 \"ctrl + d \"发送EOF。 validate (验证api文件) --api: 验证目标api文件 doc (生成文档文件) --dir: 目标目录 --o: 输出markdown目录 go (提供的api生成go文件) --api: api文件 --branch: 远程 repo 的分支,它与 --remote 一起工作。 --dir: 目标目录 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (为api文件中提供的api生成java文件) --api: api文件 --dir: 目标目录 ts (为api文件中提供的api生成ts文件) --api: api文件 --caller: 网络api调用者 --dir: 目标目录 --unwrap: 解除webapi调用器的包装,以便导入 --webapi: web api文件的路径 dart (为api文件中提供的api生成dart文件) --api: api文件 --dir: 目标目录 --hostname: 服务器的主机名 --legacy: 用于flutter v1的传统生成器 kt (为提供的api文件生成kotlin代码) --api: api文件 --dir: 目标目录 --pkg: 定义kotlin文件的包名 plugin (自定义文件生成器) --api: api文件 --dir: 目标目录 --plugin, -p: 插件文件 --style: 文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (生成Docker文件) --branch:远程版本库的分支,它与--remote一起工作。 --go:包含主函数的文件 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --port:要公开的端口,默认为无(默认:0)。 --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --scratch:使用scratch作为基础docker镜像 --tz:容器的时区(默认:亚洲/上海) --version:goctl builder golang镜像的版本。 kube (生成kubernetes文件) deploy (生成部署yaml文件) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --image:部署的docker镜像 --limitCpu:部署的cpu上限(默认为1000)。 --limitMem: 部署的内存上限(默认为1024)。 --maxReplicas: 部署的最大复制数(默认为10)。 --minReplicas: 部署的最小复制量(默认为3)。 --name:部署的名称 --namespace:部署的命名空间 --nodePort: 要公开的部署的nodePort(默认为0)。 --port: 要在pod上监听的部署的端口(默认值:0) --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote有更高的优先级。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --replicas:要部署的副本数量(默认:3个)。 --requestCpu:要部署的请求cpu(默认为500)。 --requestMem: 要部署的请求内存(默认为512)。 --revisions: 限制修订历史的数量(默认为5)。 --secret: 从注册表中提取镜像的秘密。 --serviceAccount:部署的ServiceAccount。 -o: 输出的yaml文件 rpc (生成rpc代码) new (生成rpc演示服务) --branch: 远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 --idea:命令执行环境是否来自idea插件。[可选] --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (生成proto模板) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl主路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --out, -o: proto的目标路径 --remote:模板的远程git repo,--home和--remote不能同时设置,如果有的话,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 protoc (生成grpc代码) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl主路径 --remote: 模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out:zrpc的输出目录 model (生成model代码) mysql (生成mysql模型) ddl (从ddl生成mysql模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --database, --db:数据库的名称 [可选] - --dir, -d: 目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --src, -s:ddl的路径或路径globbing模式 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c: 使用缓存生成代码 [可选] - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t:数据库中的表或表球化模式 --url:数据库的数据源,如 \"root:password@tcp(127.0.0.1:3306)/database\" pg (生成postgresql模型) datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --schema, -s:表的模式,默认为[public] --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: 数据库中的表或表球化模式 --url:数据库的数据源,如 \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (生成mongo模型) --branch:远程repo的分支,它与--remote一起工作。 --cache, -c: 使用缓存生成代码 [可选] --dir, -d:目标目录 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t:指定的模型类型名称 template (模板操作) init (初始化所有模板(强制更新)) --home: 模板的goctl主路径 clean (清理所有缓存的模板) --home: 模板的goctl主路径 update (将目标类别的模板更新为最新的) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] --home: 模板的goctl主页路径 revert (将目标模板恢复到最新版本) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] 。 --home:模板的goctl主路径 --name, -n: 模板的目标文件名 completion (生成自动补全脚本,它只适用于类unix操作系统) --name, -n:自动完成脚本的文件名,默认为[goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-api.html":{"url":"goctl-api.html","title":"api命令","keywords":"","body":"api命令 goctl api是goctl中的核心模块之一,其可以通过.api文件一键快速生成一个api服务,如果仅仅是启动一个go-zero的api演示项目, 你甚至都不用编码,就可以完成一个api服务开发及正常运行。在传统的api项目中,我们要创建各级目录,编写结构体, 定义路由,添加logic文件,这一系列操作,如果按照一条协议的业务需求计算,整个编码下来大概需要5~6分钟才能真正进入业务逻辑的编写, 这还不考虑编写过程中可能产生的各种错误,而随着服务的增多,随着协议的增多,这部分准备工作的时间将成正比上升, 而goctl api则可以完全替代你去做这一部分工作,不管你的协议要定多少个,最终来说,只需要花费10秒不到即可完成。 [!TIP] 其中的结构体编写,路由定义用api进行替代,因此总的来说,省去的是你创建文件夹、添加各种文件及资源依赖的过程的时间。 api命令说明 $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help 从上文中可以看到,根据功能的不同,api包含了很多的自命令和flag,我们这里重点说明一下 go子命令,其功能是生成golang api服务,我们通过goctl api go -h看一下使用帮助: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir 代码输出目录 --api 指定api源文件 --style 指定生成代码文件的文件名称风格,详情见文件名称命名style说明 使用示例 $ goctl api go -api user.api -dir . -style gozero 猜你想看 api语法 api目录 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"rpc命令","keywords":"","body":"rpc命令 Goctl Rpc是goctl脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。 特性 简单易用 快速提升开发效率 出错率低 贴近protoc 快速开始 方式一:快速生成greet服务 通过命令 goctl rpc new ${servieName}生成 如生成greet rpc服务: goctl rpc new greet 执行后代码结构如下: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] 新版本目录详见 rpc目录 方式二:通过指定proto生成rpc服务 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\"./user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } 生成rpc服务代码 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. 准备工作 安装了go环境 安装了protoc & protoc-gen-go,并且已经设置环境变量 更多问题请见 注意事项 用法 rpc服务生成用法 goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 参数说明 --zrpc_out 可选,默认为proto文件所在目录,生成代码的目标目录 --style 可选,输出目录的文件命名风格,详情见https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md --home 可选,指定模板路径 --remote 可选,指定模板远程仓库 --branch 可选,指定模板远程仓库分支,与 --remote 配合使用 你可以理解为 zrpc 代码生成是用 goctl rpc $protoc_command --zrpc_out=${output} 模板,如原来生成 grpc 代码指令为 $ protoc user.proto --go_out=. --go-grpc_out=. 则生成 zrpc 代码指令就为 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out 与 --go-grpc_out 生成的最终目录必须一致 --go_out & --go-grpc_out 和 --zrpc_out 的生成的最终目录必须不为同一目录,否则pb.go和_grpc.pb.go就与main函数同级了,这是不允许的。 --go_out 与 --go-grpc_out 生产的目录会受 --go_opt 和 --grpc-go_opt 和proto源文件中 go_package值的影响,要想理解这里的生成逻辑,建议阅读 官方文文档:Go Generated Code 开发人员需要做什么 关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开发人员仅需要修改 服务中的配置文件编写(etc/xx.json、internal/config/config.go) 服务中业务逻辑编写(internal/logic/xxlogic.go) 服务中资源上下文的编写(internal/svc/servicecontext.go) 注意事项 proto暂不支持多文件同时生成 proto不支持外部依赖包引入,message不支持inline 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有 // Code generated by goctl. DO NOT EDIT! // Source: xxx.proto 的标识,请注意不要在里面写业务性代码;也不要将它写在业务性代码里面。 proto import 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。 proto示例: 错误import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request和return 不支持import } 正确import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// 支持import } message Response { base.Out out = 2;// 支持import } service Greet { rpc Ping(Request) returns(Response); } 猜你想看 rpc目录 rpc配置 rpc调用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-model.html":{"url":"goctl-model.html","title":"model命令","keywords":"","body":"model命令 goctl model 为go-zero下的工具模块中的组件之一,目前支持识别mysql ddl进行model层代码生成,通过命令行或者idea插件(即将支持)可以有选择地生成带redis cache或者不带redis cache的代码逻辑。 快速开始 通过ddl生成 $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c 执行上述命令后即可快速生成CURD代码。 model ├── usermodel.go ├── usermodel_gen.go └── vars.go 通过datasource生成 $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } 用法 $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help 生成规则 默认规则 我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,对于这两个字段生成insert、update时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。 ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 生成代码仅基本的CURD结构。 缓存 对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。 缓存会缓存哪些信息? 对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。 数据有更新(update)操作会清空缓存吗? 会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。 为什么不按照单索引字段生成updateByXxx和deleteByXxx的代码? 理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。 为什么不支持findPageLimit、findAll这种模式代码生成? 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 类型转换规则 mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"plugin命令","keywords":"","body":"plugin命令 goctl支持针对api自定义插件,那我怎么来自定义一个插件了?来看看下面最终怎么使用的一个例子。 $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . 上面这个命令可以分解成如下几步: goctl 解析api文件 goctl 将解析后的结构 ApiSpec 和参数传递给goctl-android可执行文件 goctl-android 根据 ApiSpec 结构体自定义生成逻辑。 此命令前面部分 goctl api plugin -p 是固定参数,goctl-android=\"android -package com.tal\" 是plugin参数,其中goctl-android是插件二进制文件,android -package com.tal是插件的自定义参数,-api user.api -dir .是goctl通用自定义参数。 怎么编写自定义插件? go-zero框架中包含了一个很简单的自定义插件 demo,代码如下: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() 这行代码作用是解析从goctl传递过来的数据,里面包含如下部分内容: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api:定义了api文件的结构数据 Style:可选参数,可以用来控制文件命名规范 Dir:工作目录 完整的基于plugin实现的android plugin演示项目 https://github.com/zeromicro/goctl-android 猜你想看 api目录 api语法 api配置 api命令介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-other.html":{"url":"goctl-other.html","title":"其他命令","keywords":"","body":"其他命令 goctl docker goctl kube goctl docker goctl docker 可以极速生成一个 Dockerfile,帮助开发/运维人员加快部署节奏,降低部署复杂度。 准备工作 docker安装 Dockerfile 额外注意点 选择最简单的镜像:比如alpine,整个镜像5M左右 设置镜像时区RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai 多阶段构建 第一阶段构建出可执行文件,确保构建过程独立于宿主机 第二阶段将第一阶段的输出作为输入,构建出最终的极简镜像 Dockerfile编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 在 greet 项目下创建一个 hello 服务 $ goctl api new hello 文件结构如下: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go 在 hello 目录下一键生成 Dockerfile$ goctl docker -go hello.go Dockerfile 内容如下: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] 在 hello 目录下 build 镜像 $ docker build -t hello:v1 -f service/hello/Dockerfile . 查看镜像 hello v1 5455f2eaea6b 7 minutes ago 18.1MB 可以看出镜像大小约为18M。 启动服务$ docker run --rm -it -p 8888:8888 hello:v1 测试服务$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker总结 goctl 工具极大简化了 Dockerfile 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 goctl kube goctl kube提供了快速生成一个 k8s 部署文件的功能,可以加快开发/运维人员的部署进度,减少部署复杂度。 头疼编写 K8S 部署文件? K8S yaml 参数很多,需要边写边查? 保留回滚版本数怎么设? 如何探测启动成功,如何探活? 如何分配和限制资源? 如何设置时区?否则打印日志是 GMT 标准时间 如何暴露服务供其它服务调用? 如何根据 CPU 和内存使用率来配置水平伸缩? 首先,你需要知道有这些知识点,其次要把这些知识点都搞明白也不容易,再次,每次编写依然容易出错! 创建服务镜像 为了演示,这里我们以 redis:6-alpine 镜像为例。 完整 K8S 部署文件编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 一键生成 K8S 部署文件 $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 生成的 yaml 文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 部署服务,如果 adhoc namespace 不存在的话,请先通过 kubectl create namespace adhoc 创建 $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created 查看服务允许状态 $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s 测试服务$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube 总结 goctl 工具极大简化了 K8S yaml 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 猜你想看 准备工作 api目录 api语法 api配置 api命令介绍 docker介绍 k8s介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-manage.html":{"url":"template-manage.html","title":"模板管理","keywords":"","body":"模板管理 模板操作 自定义模板 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-cmd.html":{"url":"template-cmd.html","title":"模板操作","keywords":"","body":"模板操作 模板(Template)是数据驱动生成的基础,所有的代码(rest api、rpc、model、docker、kube)生成都会依赖模板, 默认情况下,模板生成器会选择内存中的模板进行生成,而对于有模板修改需求的开发者来讲,则需要将模板进行落盘, 从而进行模板修改,在下次代码生成时会加载指定路径下的模板进行生成。 使用帮助 NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help 模板初始化 NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 清除模板 NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 回滚指定分类模板 NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template 回滚模板 NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home 指定模板存储路径 模板加载 在代码生成时可以通过--home来指定模板所在文件夹,目前已支持指定模板目录的命令有: goctl api go 详情可以通过goctl api go --help查看帮助 goctl docker 详情可以通过goctl docker --help查看帮助 goctl kube 详情可以通过goctl kube --help查看帮助 goctl rpc new 详情可以通过goctl rpc new --help查看帮助 goctl rpc proto 详情可以通过goctl rpc proto --help查看帮助 goctl model mysql ddl 详情可以通过goctl model mysql ddl --help查看帮助 goctl model mysql datasource 详情可以通过goctl model mysql datasource --help查看帮助 goctl model pg datasource 详情可以通过goctl model pg datasource --help查看帮助 goctl model mongo 详情可以通过goctl model mongo --help查看帮助 默认情况(在不指定--home)会从$HOME/.goctl目录下读取。 使用示例 初始化模板到指定$HOME/template目录下$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! 使用$HOME/template模板进行greet rpc生成$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template.html":{"url":"template.html","title":"自定义模板","keywords":"","body":"模板修改 场景 实现统一格式的body响应,格式如下: { \"code\": 0, \"msg\": \"OK\", \"data\": {} // ① } ① 实际响应数据 [!TIP] go-zero生成的代码没有对其进行处理 准备工作 我们提前在module为greet的工程下的response包中写一个Response方法,目录树类似如下: greet ├── response │ └── response.go └── xxx... 代码如下 package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } 修改handler模板 $ vim ~/.goctl/api/handler.tpl 将模板替换为以下内容 package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① 替换为你真实的response包名,仅供参考 ② 自定义模板内容 [!TIP] 1.如果本地没有~/.goctl/api/handler.tpl文件,可以通过模板初始化命令goctl template init进行初始化 修改模板前后对比 修改前 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // 以下内容将被自定义模板替换 if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } 修改后 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } 修改模板前后响应体对比 修改前 { \"message\": \"Hello go-zero!\" } 修改后 { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } 总结 本文档仅对http相应为例讲述了自定义模板的流程,除此之外,自定义模板的场景还有: model 层添加kmq model 层生成待有效期option的model实例 http自定义相应格式 ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"extended-reading.html":{"url":"extended-reading.html","title":"扩展阅读","keywords":"","body":"扩展阅读 扩展阅读是对go-zero 中的最佳实现和组件的介绍, 因此会比较庞大,而此资源将会持续更新,也欢迎大家来进行文档贡献,本节将包含以下目录(按照文档更新时间排序): 快速构建高并发微服务 日志组件介绍 布隆过滤器 executors 流处理组件 fx go-zero mysql使用介绍 redis锁 periodlimit限流 令牌桶限流 时间轮介绍 熔断原理与实现 进程内缓存组件 collection.Cache 高效的关键词替换和敏感词过滤工具 服务自适应降载保护设计 文本序列化和反序列化 并发处理工具 MapReduce 基于prometheus的微服务指标监控 防止缓存击穿之进程内共享调用 DB缓存机制 zrpc 使用介绍 go-zero缓存设计之持久层缓存 go-zero缓存设计之业务层缓存 go-zero分布式定时任务 我是如何用go-zero 实现一个中台系统 流数据处理利器 10月3日线上交流问题汇总 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"logx.html":{"url":"logx.html","title":"日志组件介绍","keywords":"","body":"logx 使用示例 var c logx.LogConf // 从 yaml 文件中 初始化配置 conf.MustLoad(\"config.yaml\", &c) // logx 根据配置初始化 logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() 初始化 logx 有很多可以配置项,可以参考 logx.LogConf 中的定义。目前可以使用 logx.MustSetUp(c) 进行初始化配置,如果没有进行初始化配置,所有的配置将使用默认配置。 Level logx 支持的打印日志级别有: alert info error severe fatal slow stat 可以使用对应的方法打印出对应级别的日志。 同时为了方便调试,线上使用,可以动态调整日志打印级别,其中可以通过 logx.SetLevel(uint32) 进行级别设置,也可以通过配置初始化进行设置。目前支持的参数为: const ( // 打印所有级别的日志 InfoLevel = iota // 打印 errors, slows, stacks 日志 ErrorLevel // 仅打印 severe 级别日志 SevereLevel ) 日志模式 目前日志打印模式主要分为2种,一种文件输出,一种控制台输出。推荐方式,当采用 k8s,docker 等部署方式的时候,可以将日志输出到控制台,使用日志收集器收集导入至 es 进行日志分析。如果是直接部署方式,可以采用文件输出方式,logx 会自动在指定文件目录创建对应 5 个对应级别的的日志文件保存日志。 . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log 同时会按照自然日进行文件分割,当超过指定配置天数,会对日志文件进行自动删除,打包等操作。 禁用日志 如果不需要日志打印,可以使用 logx.Close() 关闭日志输出。注意,当禁用日志输出,将无法在次打开,具体可以参考 logx.RotateLogger 和 logx.DailyRotateRule 的实现。 关闭日志 因为 logx 采用异步进行日志输出,如果没有正常关闭日志,可能会造成部分日志丢失的情况。必须在程序退出的地方关闭日志输出: logx.Close() 框架中 rest 和 zrpc 等大部分地方已经做好了日志配置和关闭相关操作,用户可以不用关心。 同时注意,当关闭日志输出之后,将无法在次打印日志了。 推荐写法: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration 我们打印日志的时候可能需要打印耗时情况,可以使用 logx.WithDuration(time.Duration), 参考如下示例: startTime := timex.Now() // 数据库查询 rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } 会输出如下格式 {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} 这样就可以很容易统计出慢 sql 相关信息。 TraceLog tracingEntry 是为了链路追踪日志输出定制的。可以打印 context 中的 traceId 和 spanId 信息,配合我们的 rest 和 zrpc 很容易完成链路日志的相关打印。示例如下 logx.WithContext(context.Context).Info(\"This is info!\") SysLog 应用中可能有部分采用系统 log 进行日志打印,logx 同样封装方法,很容易将 log 相关的日志收集到 logx 中来。 logx.CollectSysLog() 日志配置相关 LogConf 定义日志系统所需的基本配置 完整定义如下: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode 定义了日志打印的方式。默认的模式是 console, 打印到控制台上面。 目前支持的模式如下: console 打印到控制台 file 打印到指定路径下的access.log, error.log, stat.log等文件里 volume 为了在k8s内打印到mount进来的存储上,因为多个pod可能会覆盖相同的文件,volume模式自动识别pod并按照pod分开写各自的日志文件 Path Path 定义了文件日志的输出路径,默认值为 logs。 Level Level 定义了日志打印级别,默认值为 info。 目前支持的级别如下: info error severe Compress Compress 定义了日志是否需要压缩,默认值为 false。在 Mode 为 file 模式下面,文件最后会进行打包压缩成 .gz 文件。 KeepDays KeepDays 定义日志最大保留天数,默认值为 0,表示不会删除旧的日志。在 Mode 为 file 模式下面,如果超过了最大保留天数,旧的日志文件将会被删除。 StackCooldownMillis StackCooldownMillis 定义了日志输出间隔,默认为 100 毫秒。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"bloom.html":{"url":"bloom.html","title":"布隆过滤器","keywords":"","body":"bloom go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 布隆过滤器bloom 在做服务器开发的时候,相信大家有听过布隆过滤器,可以判断某元素在不在集合里面,因为存在一定的误判和删除复杂问题,一般的使用场景是:防止缓存击穿(防止恶意攻击)、 垃圾邮箱过滤、cache digests 、模型检测器等、判断是否存在某行数据,用以减少对磁盘访问,提高服务的访问性能。 go-zero 提供的简单的缓存封装 bloom.bloom,简单使用方式如下 // 初始化 redisBitSet store := redis.New(\"redis 地址\", func(r *redis.Redis) { r.Type = redis.NodeType }) // 声明一个bitSet, key=\"test_key\"名且bits是1024位 bitSet := newRedisBitSet(store, \"test_key\", 1024) // 判断第0位bit存不存在 isSetBefore, err := bitSet.check([]uint{0}) // 对第512位设置为1 err = bitSet.set([]uint{512}) // 3600秒后过期 err = bitSet.expire(3600) // 删除该bitSet err = bitSet.del() bloom 简单介绍了最基本的redis bitset 的使用。下面是真正的bloom实现。 对元素hash 定位 // 对元素进行hash 14次(const maps=14),每次都在元素后追加byte(0-13),然后进行hash. // 将locations[0-13] 进行取模,最终返回locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i 向bloom里面add 元素 // 我们可以发现 add方法使用了getLocations和bitSet的set方法。 // 我们将元素进行hash成长度14的uint切片,然后进行set操作存到redis的bitSet里面。 func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } 检查bloom里面是否有某元素 // 我们可以发现 Exists方法使用了getLocations和bitSet的check方法 // 我们将元素进行hash成长度14的uint切片,然后进行bitSet的check验证,存在返回true,不存在或者check失败返回false func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } 本节主要介绍了go-zero框架中的 core.bloom 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors 在 go-zero 中,executors 充当任务池,做多任务缓冲,适用于做批量处理的任务。如:clickhouse 大批量 insert,sql batch insert。同时也可以在 go-queue 中看到 executors 【在 queue 里面使用的是 ChunkExecutor ,限定任务提交字节大小】。 所以当你存在以下需求,都可以使用这个组件: 批量提交任务 缓冲一部分任务,惰性提交 延迟任务提交 具体解释之前,先给一个大致的概览图: 接口设计 在 executors 包下,有如下几个 executor : Name Margin value bulkexecutor 达到 maxTasks 【最大任务数】 提交 chunkexecutor 达到 maxChunkSize【最大字节数】提交 periodicalexecutor basic executor delayexecutor 延迟执行传入的 fn() lessexecutor 你会看到除了有特殊功能的 delay,less ,其余 3 个都是 executor + container 的组合设计: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // 选项模式:在 go-zero 中多处出现。在多配置下,比较好的设计思路 // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute 真正做执行的函数] [maxTasks 执行临界点] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. 可以看出 bulkexecutor 底层依赖 periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } 而这个 container是个 interface: TaskContainer interface { // 把 task 加入 container AddTask(task interface{}) bool // 实际上是去执行传入的 execute func() Execute(tasks interface{}) // 达到临界值,移除 container 中全部的 task,通过 channel 传递到 execute func() 执行 RemoveAll() interface{} } 由此可见之间的依赖关系: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] 所以你想完成自己的 executor,可以实现 container 的这 3 个接口,再结合 periodicalexecutor 就行 所以回到👆那张图,我们的重点就放在 periodicalexecutor,看看它是怎么设计的? 如何使用 首先看看如何在业务中使用这个组件: 现有一个定时服务,每天固定时间去执行从 mysql 到 clickhouse 的数据同步: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } 初始化 bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() 是真正insert执行函数【需要开发者自己编写具体业务逻辑】 dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // 3s会自动刷一次container中task去执行 executors.WithBulkTasks(10240), // container最大task数。一般设为2的幂次 ) } [!TIP] 额外介绍一下:clickhouse 适合大批量的插入,因为 insert 速度很快,大批量 insert 更能充分利用 clickhouse 主体业务逻辑编写: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] 可能会疑惑为什么要 Flush(), Wait() ,后面会通过源码解析一下 使用上总体分为 3 步: Add():加入 task Flush():刷新 container 中的 task Wait():等待全部 task 执行完成 源码分析 [!TIP] 此处主要分析 periodicalexecutor,因为其他两个常用的 executor 都依赖它 初始化 func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander:传递 tasks 的 channel container:暂存 Add() 的 task confirmChan:阻塞 Add() ,在开始本次的 executeTasks() 会放开阻塞 ticker:定时器,防止 Add() 阻塞时,会有一个定时执行的机会,及时释放暂存的 task Add() 初始化完,在业务逻辑的第一步就是把 task 加入 executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask 将container中tasks pop, return if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } addAndCheck() 中 AddTask() 就是在控制最大 tasks 数,如果超过就执行 RemoveAll() ,将暂存 container 的 tasks pop,传递给 commander ,后面有 goroutine 循环读取,然后去执行 tasks。 backgroundFlush() 开启一个后台协程,对 container 中的 task,不断刷新: func (pe *PeriodicalExecutor) backgroundFlush() { // 封装 go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // 从channel拿到 []tasks case vals := pe.interval*idleRound { // 既没到maxTask,Flush() err,并且 last->now 时间过长,会再次触发 Flush() // 只有这置反,才会开启一个新的 backgroundFlush() 后台协程 pe.guarded = false // 再次刷新,防止漏掉 pe.Flush() return } } } }) } 总体两个过程: commander 接收到 RemoveAll() 传递来的 tasks,然后执行,并放开 Add() 的阻塞,得以继续 Add() ticker 到时间了,如果第一步没有执行,则自动 Flush() ,也会去做 task 的执行 Wait() 在 backgroundFlush() ,提到一个函数:enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } 这样列举就知道为什么之前在最后要带上 dts.insertExecutor.Wait(),当然要等待全部的 goroutine task 完成。 思考 在看源码中,思考了一些其他设计上的思路,大家是否也有类似的问题: 在分析 executors 中,会发现很多地方都有 lock [!TIP] go test 存在竞态,使用加锁来避免这种情况 在分析 confirmChan 时发现,confirmChan 在此次提交才出现,为什么会这么设计? 之前是:wg.Add(1) 是写在 executeTasks() ;现在是:先wg.Add(1),再放开 confirmChan 阻塞 如果 executor func 执行阻塞,Add task 还在进行,因为没有阻塞,可能很快执行到 Executor.Wait(),这时就会出现 wg.Wait() 在 wg.Add() 前执行,这会 panic 具体可以看最新版本的TestPeriodicalExecutor_WaitFast() ,不妨跑在此版本上,就可以重现 总结 剩余还有几个 executors 的分析,就留给大家去看看源码。 总之,整体设计上: 遵循面向接口设计 灵活使用 channel ,waitgroup 等并发工具 执行单元+存储单元的搭配使用 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"fx.html":{"url":"fx.html","title":"流处理组件 fx","keywords":"","body":"数据的流处理利器 流处理(Stream processing)是一种计算机编程范式,其允许给定一个数据序列(流处理数据源),一系列数据操作(函数)被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到Kafka,然后再由Job消费Kafaka写到elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx gozero是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce工具进行处理。 总结 本篇文章介绍了流处理的基本概念和gozero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mysql.html":{"url":"mysql.html","title":"go-zero mysql使用介绍","keywords":"","body":"mysql go-zero 提供更易于操作的 mysql API。 [!TIP] 但是 stores/mysql 定位不是一个 orm 框架,如果你需要通过 sql/scheme -> model/struct 逆向生成 model 层代码,可以使用「goctl model」,这个是极好的功能。 Feature 相比原生,提供对开发者更友好的 API 完成 queryField -> struct 的自动赋值 批量插入「bulkinserter」 自带熔断 API 经过若干个服务的不断考验 提供 partial assignment 特性,不强制 struct 的严格赋值 Connection 下面用一个例子简单说明一下如何创建一个 mysql 连接的 model: // 1. 快速连接一个 mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. 在 servicecontext 中调用,懂model上层的logic层调用 model.NewMysqlModel(heraMysql, tablename), // 3. model层 mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. 创建一个批量insert的 [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD 准备一个 User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` // 头像 UserName string `db:\"user_name\"` // 姓名 Sex int `db:\"sex\"` // 1男,2女 MobilePhone string `db:\"mobile_phone\"` // 手机号 } 其中 userBuilderQueryRows : go-zero 中提供 struct -> [field...] 的转化,开发者可以将此当成模版直接使用。 insert // 一个实际的insert model层操作 func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } 拼接 insertsql 将 insertsql 以及占位符对应的 struct field 传入 -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...) : args... 需对应 sql 中的占位符。不然会出现赋值异常的问题。 go-zero 将涉及 mysql 修改的操作统一抽象为 Exec() 。所以 insert/update/delete 操作本质上是一致的。其余两个操作,开发者按照上述 insert 流程尝试即可。 query 只需要传入 querysql 和 model 结构体,就可以获取到被赋值好的 model 。无需开发者手动赋值。 func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } 声明 model struct ,拼接 querysql conn.QueryRow(&model, querysql, args...) : args... 与 querysql 中的占位符对应。 [!WARNING] QueryRow() 中第一个参数需要传入 Ptr 「底层需要反射对 struct 进行赋值」 上述是查询一条记录,如果需要查询多条记录时,可以使用 conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } 与 QueryRow() 不同的地方在于: model 需要设置成 Slice ,因为是查询多行,需要对多个 model 赋值。但同时需要注意️:第一个参数需要传入 Ptr querypartial 从使用上,与上述的 QueryRow() 无异「这正体现了 go-zero 高度的抽象设计」。 区别: QueryRow() : len(querysql fields) == len(struct) ,且一一对应 QueryRowPartial() :len(querysql fields) numA:数据库字段数;numB:定义的 struct 属性数。 如果 numA ,但是你恰恰又需要统一多处的查询时「定义了多个 struct 返回不同的用途,恰恰都可以使用相同的 querysql 」,就可以使用 QueryRowPartial() 事务 要在事务中执行一系列操作,一般流程如下: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // 返回任何错误都会回滚事务 if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // 还可以继续执行 insert/update/delete 相关操作 return nil }) 如同上述例子,开发者只需将 事务 中的操作都包装在一个函数 func(session sqlx.Session) error {} 中即可,如果事务中的操作返回任何错误, Transact() 都会自动回滚事务。 分布式事务 go-zero 与 dtm 深度合作,原生的支持了分布式事务,详情参见 分布式事务支持 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis锁","keywords":"","body":"redis-lock redis lock 既然是锁,首先想到的一个作用就是:防重复点击,在一个时间点只有一个请求产生效果。 而既然是 redis,就得具有排他性,同时也具有锁的一些共性: 高性能 不能出现死锁 不能出现节点down掉后加锁失败 go-zero 中利用 redis set key nx 可以保证key不存在时写入成功,px 可以让key超时后自动删除「最坏情况也就是超时自动删除key,从而也不会出现死锁」 example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. 可选操作,设置 redislock 过期时间 redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"当前有其他用户正在进行操作,请稍后重试\") } defer func() { recover() // 3. 释放锁 redisLock.Release() }() 和你在使用 sync.Mutex 的方式时一致的。加锁解锁,执行你的业务操作。 获取锁 lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } 先介绍几个 redis 的命令选项,以下是为 set 命令增加的选项: ex seconds :设置key过期时间,单位s px milliseconds :设置key过期时间,单位毫秒 nx:key不存在时,设置key的值 xx:key存在时,才会去设置key的值 其中 lua script 涉及的入参: args 示例 含义 KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD 唯一标识:随机字符串 ARGV[2] 30000 设置锁的过期时间 然后来说说代码特性: Lua 脚本保证原子性「当然,把多个操作在 Redis 中实现成一个操作,也就是单命令操作」 使用了 set key value px milliseconds nx value 具有唯一性 加锁时首先判断 key 的 value 是否和之前设置的一致,一致则修改过期时间 释放锁 delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } 释放锁的时候只需要关注一点: 不能释放别人的锁,不能释放别人的锁,不能释放别人的锁 所以需要先 get(key) == value「key」,为 true 才会去 delete Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit限流","keywords":"","body":"periodlimit 不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性。即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪。 本文就来介绍一下 periodlimit 。 使用 const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero 采取 滑动窗口 计数的方式,计算一段时间内对同一个资源的访问次数,如果超过指定的 limit ,则拒绝访问。当然如果你是在一段时间内访问不同的资源,每一个资源访问量都不超过 limit ,此种情况是允许大量请求进来的。 而在一个分布式系统中,存在多个微服务提供服务。所以当瞬间的流量同时访问同一个资源,如何让计数器在分布式系统中正常计数? 同时在计算资源访问时,可能会涉及多个计算,如何保证计算的原子性? go-zero 借助 redis 的 incrby 做资源访问计数 采用 lua script 做整个窗口计算,保证计算的原子性 下面来看看 lua script 控制的几个关键属性: argument mean key[1] 访问资源的标示 ARGV[1] limit => 请求总数,超过则限速。可设置为 QPS ARGV[2] window大小 => 滑动窗口,用 ttl 模拟出滑动的效果 -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- 如果是第一次访问,设置过期时间 => TTL = window size -- 因为是只限制一段时间的访问次数 if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current 至于上述的 return code ,返回给调用方。由调用方来决定请求后续的操作: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit 下面这张图描述了请求进入的过程,以及请求触发 limit 时后续发生的情况: 后续处理 如果在服务某个时间点,请求大批量打进来,periodlimit 短期时间内达到 limit 阈值,而且设置的时间范围还远远没有到达。后续请求的处理就成为问题。 periodlimit 中并没有处理,而是返回 code 。把后续请求的处理交给了开发者自己处理。 如果不做处理,那就是简单的将请求拒绝 如果需要处理这些请求,开发者可以借助 mq 将请求缓冲,减缓请求的压力 采用 tokenlimit,允许暂时的流量冲击 所以下一篇我们就来聊聊 tokenlimit 总结 go-zero 中的 periodlimit 限流方案是基于 redis 计数器,通过调用 redis lua script ,保证计数过程的原子性,同时保证在分布式的情况下计数是正常的。但是这种方案存在缺点,因为它要记录时间窗口内的所有行为记录,如果这个量特别大的时候,内存消耗会变得非常严重。 参考 go-zero periodlimit 分布式服务限流实战,已经为你排好坑了 tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"令牌桶限流","keywords":"","body":"tokenlimit 本节将通过令牌桶限流(tokenlimit)来介绍其基本使用。 使用 const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit 从整体上令牌桶生产token逻辑如下: 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中; 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃; 当流量以速率v进入,从桶中以速率v取令牌,拿到令牌的流量通过,拿不到令牌流量不通过,执行熔断逻辑; go-zero 在两类限流器下都采取 lua script 的方式,依赖redis可以做到分布式限流,lua script同时可以做到对 token 生产读取操作的原子性。 下面来看看 lua script 控制的几个关键属性: argument mean ARGV[1] rate 「每秒生成几个令牌」 ARGV[2] burst 「令牌桶最大值」 ARGV[3] now_time「当前时间戳」 ARGV[4] get token nums 「开发者需要获取的token数」 KEYS[1] 表示资源的tokenkey KEYS[2] 表示刷新时间的key -- 返回是否可以活获得预期的token local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:需要填满 token_bucket 需要多久 local fill_time = capacity/rate -- 将填充时间向下取整 local ttl = math.floor(fill_time*2) -- 获取目前 token_bucket 中剩余 token 数 -- 如果是第一次进入,则设置 token_bucket 数量为 令牌桶最大值 local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- 上一次更新 token_bucket 的时间 local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- 通过当前时间与上一次更新时间的跨度,以及生产token的速率,计算出新的token数 -- 如果超过 max_burst,多余生产的token会被丢弃 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- 更新新的token数,以及更新时间 redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed 上述可以看出 lua script :只涉及对 token 操作,保证 token 生产合理和读取合理。 函数分析 从上述流程中看出: 有多重保障机制,保证限流一定会完成。 如果redis limiter失效,至少在进程内rate limiter兜底。 重试 redis limiter 机制保证尽可能地正常运行。 总结 go-zero 中的 tokenlimit 限流方案适用于瞬时流量冲击,现实请求场景并不以恒定的速率。令牌桶相当预请求,当真实的请求到达不至于瞬间被打垮。当流量冲击到一定程度,则才会按照预定速率进行消费。 但是生产token上,不能按照当时的流量情况作出动态调整,不够灵活,还可以进行进一步优化。此外可以参考Token bucket WIKI 中提到分层令牌桶,根据不同的流量带宽,分至不同排队中。 参考 go-zero tokenlimit Go-Redis 提供的分布式限流库 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"时间轮介绍","keywords":"","body":"TimingWheel 本文来介绍 go-zero 中 延迟操作。延迟操作,可以采用两个方案: Timer:定时器维护一个优先队列,到时间点执行,然后把需要执行的 task 存储在 map 中 collection 中的 timingWheel ,维护一个存放任务组的数组,每一个槽都维护一个存储task的双向链表。开始执行时,计时器每隔指定时间执行一个槽里面的tasks。 方案2把维护task从 优先队列 O(nlog(n)) 降到 双向链表 O(1),而执行task也只要轮询一个时间点的tasks O(N),不需要像优先队列,放入和删除元素 O(nlog(n))。 cache 中的 timingWheel 首先我们先来在 collection 的 cache 中关于 timingWheel 的使用: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel 这是 cache 初始化中也同时初始化 timingWheel 做key的过期处理,参数依次代表: interval:时间划分刻度 numSlots:时间槽 execute:时间点执行函数 在 cache 中执行函数则是 删除过期key,而这个过期则由 timingWheel 来控制推进时间。 接下来,就通过 cache 对 timingWheel 的使用来认识。 初始化 // 真正做初始化 func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // 单个时间格时间间隔 ticker: ticker, // 定时器,做时间推动,以interval为单位推进 slots: make([]*list.List, numSlots), // 时间轮 timers: NewSafeMap(), // 存储task{key, value}的map [执行execute所需要的参数] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // 执行函数 numSlots: numSlots, // 初始化 slots num setChannel: make(chan timingEntry), // 以下几个channel是做task传递的 moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // 把 slot 中存储的 list 全部准备好 tw.initSlots() // 开启异步协程,使用 channel 来做task通信和传递 go tw.run() return tw, nil } 以上比较直观展示 timingWheel 的 “时间轮”,后面会围绕这张图解释其中推进的细节。 go tw.run() 开一个协程做时间推动: func (tw *TimingWheel) run() { for { select { // 定时器做时间推动 -> scanAndRunTasks() case 可以看出,在初始化的时候就开始了 timer 执行,并以interval时间段转动,然后底层不停的获取来自 slot 中的 list 的task,交给 execute 执行。 Task Operation 紧接着就是设置 cache key : func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } 先看在 data map 中有没有存在这个key 存在,则更新 expire -> MoveTimer() 第一次设置key -> SetTimer() 所以对于 timingWheel 的使用上就清晰了,开发者根据需求可以 add 或是 update。 同时我们跟源码进去会发现:SetTimer() MoveTimer() 都是将task输送到channel,由 run() 中开启的协程不断取出 channel 的task操作。 SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() 由上面的调用链,有一个都会调用的函数:moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => 通过key获取 [positionEntry「pos, task」] val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay 延迟时间比一个时间格间隔还小,没有更小的刻度,说明任务应该立即执行 if task.delay interval,则通过 延迟时间delay 计算其出时间轮中的 new pos, circle pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // 记录前后的移动offset。为了后面过程重新入队 timer.item.diff = pos - timer.pos } else if circle > 0 { // 转移到下一层,将 circle 转换为 diff 一部分 circle-- timer.item.circle = circle // 因为是一个数组,要加上 numSlots [也就是相当于要走到下一层] timer.item.diff = tw.numSlots + pos - timer.pos } else { // 如果 offset 提前了,此时 task 也还在第一层 // 标记删除老的 task,并重新入队,等待被执行 timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } 以上过程有以下几种情况: delay :因为 针对改变的 delay: new >= old: newCircle > 0:计算diff,并将 circle 转换为 下一层,故diff + numslots 如果只是单纯延迟时间缩短,则将老的task标记删除,重新加入list,等待下一轮loop被execute Execute 之前在初始化中,run() 中定时器的不断推进,推进的过程主要就是把 list中的 task 传给执行的 execute func。我们从定时器的执行开始看: // 定时器 「每隔 interval 会执行一次」 func (tw *TimingWheel) onTick() { // 每次执行更新一下当前执行 tick 位置 tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // 获取此时 tick位置 中的存储task的双向链表 l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } 紧接着是如何去执行 execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // 存储目前需要执行的task{key, value} [execute所需要的参数,依次传递给execute执行] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // 标记删除,在 scan 中做真正的删除 「删除map的data」 if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // 当前执行点已经过期,但是同时不在第一层,所以当前层即然已经完成了,就会降到下一层 // 但是并没有修改 pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // 因为之前已经标注了diff,需要再进入队列 next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // 以上的情况都是不能执行的情况,能够执行的会被加入tasks中 tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks,然后把每个 task->execute 执行即可 tw.runTasks(tasks) } 具体的分支情况在注释中说明了,在看的时候可以和前面的 moveTask() 结合起来,其中 circle 下降,diff 的计算是关联两个函数的重点。 至于 diff 计算就涉及到 pos, circle 的计算: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } 上面的过程可以简化成下面: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots 总结 timingWheel 靠定时器推动,时间前进的同时会取出当前时间格中 list「双向链表」的task,传递到 execute 中执行。 而时间分隔上,时间轮有 circle 分层,这样就可以不断复用原有的 numSlots ,因为定时器在不断 loop,而执行可以把上层的 slot 下降到下层,在不断 loop 中就可以执行到上层的task。 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"eco.html":{"url":"eco.html","title":"go-zero 生态","keywords":"","body":"go-zero 生态 工具中心 intellij插件 vscode插件 分布式事务支持 插件中心 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tool-center.html":{"url":"tool-center.html","title":"工具中心","keywords":"","body":"工具中心 在go-zero中,提供了很多提高工程效率的工具,如api,rpc生成,在此基础之上,api文件的编写就显得那么的无力, 因为缺少了高亮,代码提示,模板生成等,本节将带你了解go-zero是怎么解决这些难题的,本节包含以下小节: intellij插件 vscode插件 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"intellij.html":{"url":"intellij.html","title":"intellij插件","keywords":"","body":"intellij插件 Go-Zero Plugin 介绍 一款支持go-zero api语言结构语法高亮、检测以及api、rpc、model快捷生成的插件工具。 idea版本要求 IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ 版本特性 api语法高亮 api语法、语义检测 struct、route、handler重复定义检测 type跳转到类型声明位置 上下文菜单中支持api、rpc、mode相关menu选项 代码格式化(option+command+L) 代码提示 安装方式 方式一 在github的release中找到最新的zip包,下载本地安装即可。(无需解压) 方式二 在plugin商店中,搜索Goctl安装即可 预览 新建 Api(Proto) file 在工程区域目标文件夹右键->New-> New Api(Proto) File ->Empty File/Api(Proto) Template,如图: 快速生成api/rpc服务 在目标文件夹右键->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code生成 方法一(工程区域) 对应文件(api、proto、sql)右键->New->Go Zero-> Api/Rpc/Model Code,如图: 方法二(编辑区域) 对应文件(api、proto、sql)右键-> Generate-> Api/Rpc/Model Code 错误提示 Live Template Live Template可以加快我们对api文件的编写,比如我们在go文件中输入main关键字根据tip回车后会插入一段模板代码 func main(){ } 或者说看到下图你会更加熟悉,曾几何时你还在这里定义过template 下面就进入今天api语法中的模板使用说明吧,我们先来看看service模板的效果 首先上一张图了解一下api文件中几个模板生效区域(psiTree元素区域) 预设模板及生效区域 模板关键字 psiTree生效区域 描述 @doc ApiService doc注释模板 doc ApiService doc注释模板 struct Struct struct声明模板 info ApiFile info block模板 type ApiFile type group模板 handler ApiService handler文件名模板 get ApiService get方法路由模板 head ApiService head方法路由模板 post ApiService post方法路由模板 put ApiService put方法路由模板 delete ApiService delete方法路由模板 connect ApiService connect方法路由模板 options ApiService options方法路由模板 trace ApiService trace方法路由模板 service ApiFile service服务block模板 json Tag、Tag literal tag模板 xml Tag、Tag literal tag模板 path Tag、Tag literal tag模板 form Tag、Tag literal tag模板 关于每个模板对应内容可在Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags中查看详细模板内容,如json tag模板内容为 json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"vscode.html":{"url":"vscode.html","title":"vscode插件","keywords":"","body":"vs code 插件 该插件可以安装在 1.46.0+ 版本的 Visual Studio Code 上,首先请确保你的 Visual Studio Code 版本符合要求,并已安装 goctl 命令行工具。如果尚未安装 Visual Studio Code,请安装并打开 Visual Studio Code。 导航到“扩展”窗格,搜索 goctl 并安装此扩展(发布者ID为 “xiaoxin-technology.goctl”)。 Visual Studio Code 扩展使用请参考这里。 功能列表 已实现功能 语法高亮 跳转到定义/引用 代码格式化 代码块提示 未实现功能: 语法错误检查 跨文件代码跳转 goctl 命令行调用 语法高亮 代码跳转 代码格式化 调用 goctl 命令行格式化工具,使用前请确认 goctl 已加入 $PATH 且有可执行权限 代码块提示 info 代码块 type 代码块 service 代码块 handler 代码块 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"分布式事务支持","keywords":"","body":"分布式事务支持 需求场景 在微服务架构中,当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多: 订单系统:需要保证创建订单和扣减库存要么同时成功,要么同时回滚 跨行转账场景:数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败 积分兑换场景:需要保证积分扣减和权益增加同时成功,或者同时失败 出行订票场景:需要在第三方系统同时定几张票,要么同时成功,要么全部取消 面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 解决方案 go-zero与dtm强强联合,推出了在go-zero中无缝接入dtm的极简方案,是go生态中首家提供分布式事务能力的微服务框架。该方案具备以下特征: dtm服务可以通过配置,直接注册到go-zero的注册中心 go-zero能够以内建的target格式访问dtm服务器 dtm能够识别go-zero的target格式,动态访问go-zero中的服务 详细的接入方式,参见dtm文档:go-zero支持 更多应用场景 dtm不仅可以解决上述的分布式事务场景,还可以解决更多的与数据一致性相关的场景,包括: 数据库与缓存一致性: dtm 的二阶段消息,能够保证数据库更新操作,和缓存更新/删除操作的原子性 秒杀系统: dtm 能够保证秒杀场景下,创建的订单量与库存扣减数量完全一样,无需后续的人工校准 多种存储组合: dtm 已支持数据库、Redis、Mongo等多种存储,可以将它们组合为一个全局事务,保证数据的一致性 更多 dtm 的能力和介绍,参见dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"plugin-center.html":{"url":"plugin-center.html","title":"插件中心","keywords":"","body":"插件中心 goctl api提供了对plugin命令来支持对api进行功能扩展,当goctl api中的功能不满足你的使用, 或者需要对goctl api进行功能自定义的扩展,那么插件功能将非常适合开发人员进行自给自足,详情见 goctl plugin 插件资源 goctl-go-compact goctl默认的一个路由一个文件合并成一个文件 goctl-swagger 通过api文件生成swagger文档 goctl-php goctl-php是一款基于goctl的插件,用于生成 php 调用端(服务端) http server请求代码 猜你想看 goctl插件 api语法介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"learning-resource.html":{"url":"learning-resource.html","title":"学习资源","keywords":"","body":"学习资源 这里将不定期更新go-zero的最新学习资源通道,目前包含通道有: 公众号 Go夜读 Go开源说 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"wechat.html":{"url":"wechat.html","title":"公众号","keywords":"","body":"公众号 微服务实践是go-zero的官方公众号,在这里会发布最新的go-zero最佳实践,同步go夜读、go开源说、GopherChina、腾讯云开发者大会等多渠道关于go-zero的最新技术和资讯。 公众号名称 公众号作者 公众号二维码 微服务实践 kevwan 推荐主题 《带你十天轻松搞定 Go 微服务》 该系列将带你在本机利用 go-zero 快速开发一个商城系统,向大家详细展示了基于 go-zero 框架构建微服务的过程,整个系列分十篇文章,目录结构如下: 第一天:环境搭建 第二天:服务拆分 第三天:用户服务 第四天:产品服务 第五天:订单服务 第六天:支付服务 第七天:RPC服务Auth验证 第八天:服务监控 第九天:链路追踪 第十天:分布式事务 干货 这里列举一些干货,想要收获更多go-zero最佳实践干货,可以关注公众号获取最新动态。 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goreading.html":{"url":"goreading.html","title":"Go夜读","keywords":"","body":"Go夜读 2020-08-16 晓黑板 go-zero 微服务框架的架构设计 2020-10-03 go-zero 微服务框架和线上交流 防止缓存击穿之进程内共享调用 基于go-zero实现JWT认证 再见go-micro!企业项目迁移go-zero全攻略(一) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gotalk.html":{"url":"gotalk.html","title":"Go开源说","keywords":"","body":"Go开源说 Go 开源说第四期 - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-cache.html":{"url":"redis-cache.html","title":"go-zero缓存设计之持久层缓存","keywords":"","body":"go-zero缓存设计之持久层缓存 缓存设计原理 我们对缓存是只删除,不做更新,一旦DB里数据出现修改,我们就会直接删除对应的缓存,而不是去更新。 我们看看删除缓存的顺序怎样才是正确的。 先删除缓存,再更新DB 我们看两个并发请求的情况,A请求需要更新数据,先删除了缓存,然后B请求来读取数据,此时缓存没有数据,就会从DB加载数据并写回缓存,然后A更新了DB,那么此时缓存内的数据就会一直是脏数据,直到缓存过期或者有新的更新数据的请求。如图 先更新DB,再删除缓存 A请求先更新DB,然后B请求来读取数据,此时返回的是老数据,此时可以认为是A请求还没更新完,最终一致性,可以接受,然后A删除了缓存,后续请求都会拿到最新数据,如图 让我们再来看一下正常的请求流程: 第一个请求更新DB,并删除了缓存 第二个请求读取缓存,没有数据,就从DB读取数据,并回写到缓存里 后续读请求都可以直接从缓存读取 我们再看一下DB查询有哪些情况,假设行记录里有ABCDEFG七列数据: 只查询部分列数据的请求,比如请求其中的ABC,CDE或者EFG等,如图 查询单条完整行记录,如图 查询多条行记录的部分或全部列,如图 对于上面三种情况,首先,我们不用部分查询,因为部分查询没法缓存,一旦缓存了,数据有更新,没法定位到有哪些数据需要删除;其次,对于多行的查询,根据实际场景和需要,我们会在业务层建立对应的从查询条件到主键的映射;而对于单行完整记录的查询,go-zero 内置了完整的缓存管理方式。所以核心原则是:go-zero 缓存的一定是完整的行记录。 下面我们来详细介绍 go-zero 内置的三种场景的缓存处理方式: 基于主键的缓存PRIMARY KEY (`id`) 这种相对来讲是最容易处理的缓存,只需要在 redis 里用 primary key 作为 key 来缓存行记录即可。 基于唯一索引的缓存 在做基于索引的缓存设计的时候我借鉴了 database 索引的设计方法,在 database 设计里,如果通过索引去查数据时,引擎会先在 索引->主键 的 tree 里面查找到主键,然后再通过主键去查询行记录,就是引入了一个间接层去解决索引到行记录的对应问题。在 go-zero 的缓存设计里也是同样的原理。 基于索引的缓存又分为单列唯一索引和多列唯一索引: 但是对于 go-zero 来说,单列和多列只是生成缓存 key 的方式不同而已,背后的控制逻辑是一样的。然后 go-zero 内置的缓存管理就比较好的控制了数据一致性问题,同时也内置防止了缓存的击穿、穿透、雪崩问题(这些在 gopherchina 大会上分享的时候仔细讲过,见后续 gopherchina 分享视频)。 另外,go-zero 内置了缓存访问量、访问命中率统计,如下所示: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 可以看到比较详细的统计信息,便于我们来分析缓存的使用情况,对于缓存命中率极低或者请求量极小的情况,我们就可以去掉缓存了,这样也可以降低成本。 单列唯一索引如下: UNIQUE KEY `product_idx` (`product`) 多列唯一索引如下: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) 缓存代码解读 1.基于主键的缓存逻辑 具体实现代码如下: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } 这里的 Take 方法是先从缓存里去通过 key 拿数据,如果拿到就直接返回,如果拿不到,那么就通过 query 方法去 DB 读取完整行记录并写回缓存,然后再返回数据。整个逻辑还是比较简单易懂的。 我们详细看看 Take 的实现: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } Take 的逻辑如下: 用 key 从缓存里查找数据 如果找到,则返回数据 如果找不到,用 query 方法去读取数据 读到后调用 c.SetCache(key, v) 设置缓存 其中的 doTake 代码和解释如下: // v - 需要读取的数据对象 // key - 缓存key // query - 用来从DB读取完整数据的方法 // cacheVal - 用来写缓存的方法 func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // 用barrier来防止缓存击穿,确保一个进程内只有一个请求去加载key对应的数据 val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // 从cache里读取数据 if err := c.doGetCache(key, v); err != nil { // 如果是预先放进来的placeholder(用来防止缓存穿透)的,那么就返回预设的errNotFound // 如果是未知错误,那么就直接返回,因为我们不能放弃缓存出错而直接把所有请求去请求DB, // 这样在高并发的场景下会把DB打挂掉的 if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // 请求DB // 如果返回的error是errNotFound,那么我们就需要在缓存里设置placeholder,防止缓存穿透 if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // 统计DB失败 c.stat.IncrementDbFails() return nil, err } // 把数据写入缓存 if err = cacheVal(v); err != nil { logx.Error(err) } } // 返回json序列化的数据 return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // 把数据写入到传入的v对象里 return jsonx.Unmarshal(val.([]byte), v) } 2. 基于唯一索引的缓存逻辑 因为这块比较复杂,所以我用不同颜色标识出来了响应的代码块和逻辑,block 2 其实跟基于主键的缓存是一样的,这里主要讲 block 1 的逻辑。 代码块的 block 1 部分分为两种情况: 通过索引能够从缓存里找到主键,此时就直接用主键走 block 2 的逻辑了,后续同上面基于主键的缓存逻辑 通过索引无法从缓存里找到主键 通过索引从DB里查询完整行记录,如有 error,返回 查到完整行记录后,会把主键到完整行记录的缓存和索引到主键的缓存同时写到 redis 里 返回所需的行记录数据 // v - 需要读取的数据对象 // key - 通过索引生成的缓存key // keyer - 用主键生成基于主键缓存的key的方法 // indexQuery - 用索引从DB读取完整数据的方法,需要返回主键 // primaryQuery - 用主键从DB获取完整数据的方法 func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // 先通过索引查询缓存,看是否有索引到主键的缓存 if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // 如果没有索引到主键的缓存,那么就通过索引查询完整数据 primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // 通过索引查询到了完整数据,设置found,后面直接使用,不需要再从缓存读取数据了 found = true // 将主键到完整数据的映射保存到缓存里,TakeWithExpire方法已经将索引到主键的映射保存到缓存了 return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // 已经通过索引找到了数据,直接返回即可 if found { return nil } // 通过主键从缓存读取数据,如果缓存没有,通过primaryQuery方法从DB读取并回写缓存再返回数据 return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } 我们来看一个实际的例子 func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // 生成基于索引的key indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // 基于主键生成完整数据缓存的key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // 基于索引的DB查询方法 func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // 基于主键的DB查询方法 func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // 错误处理,需要判断是否返回的是sqlc.ErrNotFound,如果是,我们用本package定义的ErrNotFound返回 // 避免使用者感知到有没有使用缓存,同时也是对底层依赖的隔离 switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } 所有上面这些缓存的自动管理代码都是可以通过 goctl 自动生成的,我们团队内部 CRUD 和缓存基本都是通过 goctl 自动生成的,可以节省大量开发时间,并且缓存代码本身也是非常容易出错的,即使有很好的代码经验,也很难每次完全写对,所以我们推荐尽可能使用自动的缓存代码生成工具去避免错误。 猜你想看 Go开源说第四期-go-zero缓存如何设计 Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"go-zero缓存设计之业务层缓存","keywords":"","body":"go-zero缓存设计之业务层缓存 在上一篇go-zero缓存设计之持久层缓存介绍了db层缓存,回顾一下,db层缓存主要设计可以总结为: 缓存只删除不更新 行记录始终只存储一份,即主键对应行记录 唯一索引仅缓存主键值,不直接缓存行记录(参考mysql索引思想) 防缓存穿透设计,默认一分钟 不缓存多行记录 前言 在大型业务系统中,通过对持久层添加缓存,对于大多数单行记录查询,相信缓存能够帮持久层减轻很大的访问压力,但在实际业务中,数据读取不仅仅只是单行记录, 面对大量多行记录的查询,这对持久层也会造成不小的访问压力,除此之外,像秒杀系统、选课系统这种高并发的场景,单纯靠持久层的缓存是不现实的,本节我们来 介绍go-zero实践中的缓存设计——biz缓存。 适用场景举例 选课系统 内容社交系统 秒杀 ... 像这些系统,我们可以在业务层再增加一层缓存来存储系统中的关键信息,如选课系统中学生选课信息,课程剩余名额;内容社交系统中某一段时间之间的内容信息等。 接下来,我们一内容社交系统来进行举例说明。 在内容社交系统中,我们一般是先查询一批内容列表,然后点击某条内容查看详情, 在没有添加biz缓存前,内容信息的查询流程图应该为: 从图以及上一篇文章go-zero缓存设计之持久层缓存中我们可以知道,内容列表的获取是没办法依赖缓存的, 如果我们在业务层添加一层缓存用来存储列表中的关键信息(甚至完整信息),那么多行记录的访问不在是一个问题,这就是biz redis要做的事情。 接下来我们来看一下设计方案,假设内容系统中单行记录包含以下字段 字段名称 字段类型 备注 id string 内容id title string 标题 content string 详细内容 createTime time.Time 创建时间 我们的目标是获取一批内容列表,而尽量避免内容列表走db造成访问压力,首先我们采用redis的sort set数据结构来存储,根需要存储的字段信息量,有两种redis存储方案: 缓存局部信息 对其关键字段信息(如:id等)按照一定规则压缩,并存储,score我们用createTime毫秒值(时间值相等这里不讨论),这种存储方案的好处是节约redis存储空间, 那另一方面,缺点就是需要对列表详细内容进行二次回查(但这次回查是会利用到持久层的行记录缓存的) 缓存完整信息 对发布的所有内容按照一定规则压缩后均进行存储,同样score我们还是用createTime毫秒值,这种存储方案的好处是业务的增、删、查、改均走redis,而db层这时候 就可以不用考虑行记录缓存了,持久层仅提供数据备份和恢复使用,从另一方面来看,其缺点也很明显,需要的存储空间、配置要求更高,费用也会随之增大。 示例代码: type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent 提供内容存储 func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent 提供内容删除 func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // 内容压缩 func compress(c *Content) string { // todo: do it yourself var ret string return ret } // 内容解压 func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime提供根据时间段进行数据查询 func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } 在以上例子中,redis是没有设置过期时间的,我们将增、删、改、查操作均同步到redis,我们认为内容社交系统的列表访问请求是比较高的情况下才做这样的方案设计, 除此之外,还有一些数据访问,没有想内容设计系统这么频繁的访问, 可能是某一时间段内访问量突如其来的增加,之后可能很长一段时间才会再访问一次,以此间隔, 或者说不会再访问了,面对这种场景,如果我又该如何考虑缓存的设计呢?在go-zero内容实践中,有两种方案可以解决这种问题: 增加内存缓存:通过内存缓存来存储当前可能突发访问量比较大的数据,常用的存储方案采用map数据结构来存储,map数据存储实现比较简单,但缓存过期处理则需要增加 定时器来出来,另一宗方案是通过go-zero库中的 Cache ,其是专门 用于内存管理. 采用biz redis,并设置合理的过期时间 总结 以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务 监控来衡量时候需要引入biz。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-queue.html":{"url":"go-queue.html","title":"go-zero分布式定时任务","keywords":"","body":" go-zero 分布式定时任务 日常任务开发中,我们会有很多异步、批量、定时、延迟任务要处理,go-zero中有go-queue,推荐使用go-queue去处理,go-queue本身也是基于go-zero开发的,其本身是有两种模式 dq : 依赖于beanstalkd,分布式,可存储,延迟、定时设置,关机重启可以重新执行,消息不会丢失,使用非常简单,go-queue中使用了redis setnx保证了每条消息只被消费一次,使用场景主要是用来做日常任务使用 kq:依赖于kafka,这个就不多介绍啦,大名鼎鼎的kafka,使用场景主要是做消息队列 我们主要说一下dq,kq使用也一样的,只是依赖底层不同,如果没使用过beanstalkd,没接触过beanstalkd的可以先google一下,使用起来还是挺容易的。 etc/job.yaml : 配置文件 Name: job Log: ServiceName: job Level: info #dq依赖Beanstalks、redis ,Beanstalks配置、redis配置 DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go :解析dq对应etc/*.yaml配置 /** * @Description 配置文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : 负责注册多任务 /** * @Description 注册job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) } ProducerLogic: 其中一个job业务逻辑 /** * @Description 生产者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i 另外一个Job业务逻辑 /** * @Description 消费者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description 配置 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go启动文件 /** * @Description 启动文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() //配置 var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) //注册job group := service.NewServiceGroup() handler.RegisterJob(ctx,group) //捕捉信号 ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { for { s := 常见问题: 为什么使用dq,需要使用redis? 因为beanstalk是单点服务,无法保证高可用。dq可以使用多个单点beanstalk服务,互相备份 & 保证高可用。使用redis解决重复消费问题。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"datacenter.html":{"url":"datacenter.html","title":"我是如何用go-zero 实现一个中台系统","keywords":"","body":"我是如何用 go-zero 实现一个中台系统 作者:Jack Luo 原文连接:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] 最近发现golang社区里出了一个新星的微服务框架,来自好未来,光看这个名字,就很有奔头,之前,也只是玩过go-micro,其实真正的还没有在项目中运用过,只是觉得 微服务,grpc 这些很高大尚,还没有在项目中,真正的玩过,我看了一下官方提供的工具真的很好用,只需要定义好,舒适文件jia结构 都生成了,只需要关心业务,加上最近 有个投票的活动,加上最近这几年中台也比较火,所以决定玩一下, 开源地址: https://github.com/jackluo2012/datacenter 先聊聊中台架构思路吧: 中台的概念大概就是把一个一个的app 统一起来,反正我是这样理解的。 先聊用户服务吧,现在一个公司有很多的公众号、小程序、微信的、支付宝的,还有 xxx xxx,很多的平台,每次开发的时候,我们总是需要做用户登陆的服务,不停的复制代码,然后我们就在思考能不能有一套独立的用户服务,只需要告诉我你需要传个你要登陆的平台(比如微信),微信登陆,需要的是客户端返回给服务端一个code ,然后服务端拿着这个code去微信获取用户信息,反正大家都明白。 我们决定,将所有的信息弄到配置公共服务中去,里面再存微信、支付宝以及其它平台的appid、appkey、还有支付的appid、appkey,这样就写一套。 最后说说实现吧,整个就一个repo: 网关,我们用的是: go-zero的Api服务 其它它的是服务,我们就是用的go-zero的rpc服务 看下目录结构 整个项目完成,我一个人操刀,写了1个来星期,我就实现了上面的中台系统。 datacenter-api服务 先看官方文档 https://go-zero.dev/cn/ 我们先把网关搭建起来: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter 查看book目录: ➜ datacenter tree . └── go.mod 0 directories, 1 file 创建api文件 ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api ├── user.api #用户 ├── votes.api #投票 ├── search.api #搜索 ├── questions.api #问答 └── go.mod 定义api服务 分别包含了上面的 公共服务,用户服务,投票活动服务 datacenter.api的内容: info( title: \"中台系统\"// TODO: add title desc: \"中台系统\"// TODO: add description author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) import \"user.api\" import \"votes.api\" import \"search.api\" import \"questions.api\" //获取 应用信息 type Beid { Beid int64 `json:\"beid\"` } type Token { Token string `json:\"token\"` } type WxTicket { Ticket string `json:\"ticket\"` } type Application { Sname string `json:\"Sname\"` //名称 Logo string `json:\"logo\"` // login Isclose int64 `json:\"isclose\"` //是否关闭 Fullwebsite string `json:\"fullwebsite\"` // 全站名称 } type SnsReq { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 BackUrl string `json:\"back_url\"` //登陆返回的地址 } type SnsResp { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 Appid string `json:\"appid\"` //sns 平台的id Title string `json:\"title\"` //名称 LoginUrl string `json:\"login_url\"` //微信登陆的地址 } type WxShareResp { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"获取站点的信息\" ) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"获取站点的社交属性信息\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) //获取分享的 @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } //上传需要登陆 @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"七牛上传凭证\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } user.api内容 //注册请求 type RegisterReq struct { // TODO: add members here and delete this comment Mobile string `json:\"mobile\"` //基本一个手机号码就完事 Password string `json:\"password\"` Smscode string `json:\"smscode\"` //短信码 } //登陆请求 type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` //1.密码登陆,2.短信登陆 Password string `json:\"password\"` } //微信登陆 type WxLoginReq struct { Beid int64 `json:\"beid\"` //应用id Code string `json:\"code\"` //微信登陆密钥 Ptyid int64 `json:\"ptyid\"` //对应平台 } //返回用户信息 type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } //返回APPUser type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service datacenter-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service datacenter-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } votes.api 投票内容 // 投票活动api type Actid struct { Actid int64 `json:\"actid\"` //活动id } type VoteReq struct { Aeid int64 `json:\"aeid\"` // 作品id Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` //投票票数 Viewcount int64 `json:\"viewcount\"` //浏览数 } // 活动返回的参数 type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` //活动名称 Descr string `json:\"descr\"` //活动描述 StartDate int64 `json:\"start_date\"` //活动时间 EnrollDate int64 `json:\"enroll_date\"` //投票时间 EndDate int64 `json:\"end_date\"` //活动结束时间 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 Type int64 `json:\"type\"` //投票方式 Num int64 `json:\"num\"` //投票几票 } //报名 type EnrollReq struct { Actid Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 } // 作品返回 type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` // 作品id Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 } @server( group: votes ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"活动访问+1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"获取报名的投票作品信息\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"获取报名的投票作品列表\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service datacenter-api { @doc( summary: \"投票\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } questions.api 问答内容: // 问答 抽奖 开始 @server( group: questions ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activitiesInfo get /questions/activities/info (Actid) returns (ActivityResp) @doc( summary: \"获取奖品信息\" ) @handler awardInfo get /questions/award/info (Actid) returns (ActivityResp) @handler awardList get /questions/award/list (Actid) returns (ActivityResp) } type AnswerReq struct { ActivityId int64 `json:\"actid\"` Answers string `json:\"answers\"` Score string `json:\"score\"` } type QuestionsAwardReq struct { ActivityId int64 `json:\"actid\"` AnswerId int64 `json:\"answerid\"` } type AnswerResp struct { Answers string `json:\"answers\"` Score string `json:\"score\"` } type AwardConvertReq struct { UserName string `json:\"username\"` Phone string `json:\"phone\"` LotteryId int64 `json:\"lotteryid\"` } @server( jwt: Auth group: questions middleware: Usercheck ) service datacenter-api { @doc( summary: \"获取题目\" ) @handler lists get /questions/lists (VoteReq) returns (AnswerResp) @doc( summary: \"提交答案\" ) @handler change post /questions/change (AnswerReq) returns (VoteResp) @doc( summary: \"获取分数\" ) @handler grade get /questions/grade (VoteReq) returns (VoteResp) @doc( summary: \"开始转盘\" ) @handler turntable post /questions/lottery/turntable (EnrollReq) returns (EnrollResp) @doc( summary: \"填写中奖信息人\" ) @handler lottery post /questions/lottery/convert (AwardConvertReq) returns (EnrollResp) } // 问答 抽奖 结束 search.api 搜索 type SearchReq struct { Keyword string `json:\"keyword\"` Page string `json:\"page\"` Size string `json:\"size\"` } type SearchResp struct { Data []ArticleReq `json:\"data\"` } type ArticleReq struct{ NewsId string `json:\"NewsId\"` NewsTitle string `json:\"NewsTitle\"` ImageUrl string `json:\"ImageUrl\"` } @server( group: search middleware: Admincheck ) service datacenter-api { @doc( summary: \"搜索\" ) @handler article get /search/article (SearchReq) returns (SearchResp) @handler articleInit get /search/articel/init (SearchReq) returns (SearchResp) @handler articleStore post /search/articel/store (ArticleReq) returns (ArticleReq) } 上面基本上写就写的API及文档的思路 生成datacenter api服务 ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter treer . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files 我们打开 etc/datacenter-api.yaml 把必要的配置信息加上 Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: 你的jwtwon Secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: 密码 Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc 上面的 UserRpc, CommonRpc ,还有 VotesRpc 这些我先写上,后面再来慢慢加。 我们先来写 CommonRpc 服务。 CommonRpc服务 新建项目目录 ➜ datacenter mkdir -p common/rpc && cd common/rpc 直接就新建在了,datacenter目录中,因为common 里面,可能以后会不只会提供rpc服务,可能还有api的服务,所以又加了rpc目录 goctl创建模板 ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto 往里面填入内容: ➜ rpc cat common.proto syntax = \"proto3\"; option go_package = \"common\"; package common; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } // 请求的api message AppConfigReq { int64 beid=1; int64 ptyid=2; } // 返回的值 message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl生成rpc服务 ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files 基本上,就把所有的目录规范和结构的东西都生成了,就不用纠结项目目录了,怎么放了,怎么组织了。 看一下,配置信息,里面可以写入mysql和其它redis的信息: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc 我们再来加上数据库服务: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files 这样基本的一个 rpc 就写完了,然后我们将rpc 和model 还有api串连起来,这个官方的文档已经很详细了,这里就只是贴一下代码: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } 再在svc中修改: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } 上面的代码已经将 rpc 和 model 数据库关联起来了,我们现在再将 rpc 和 api 关联起来: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } 加入 svc 服务中: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"调用 %s 方法 耗时: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } 这样基本上,我们就可以在 logic 的文件目录中调用了: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { //检查 缓存中是否有值 err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } 这样,基本就连接起来了,其它基本上就不用改了,UserRPC, VotesRPC 类似,这里就不在写了。 使用心得 go-zero 的确香,因为它有一个 goctl 的工具,他可以自动的把代码结构全部的生成好,我们就不再去纠结,目录结构 ,怎么组织,没有个好几年的架构能力是不好实现的,有什么规范那些,并发,熔断,完全不用,考虑其它的,专心的实现业务就好,像微服务,还要有服务发现,一系列的东西,都不用关心,因为 go-zero 内部已经实现了。 我写代码也写了有10多年了,之前一直用的 php,比较出名的就 laravel,thinkphp,基本上就是模块化的,像微服务那些实现真的有成本,但是你用上go-zero,你就像调api接口一样简单的开发,其它什么服务发现,那些根本就不用关注了,只需要关注业务。 一个好的语言,框架,他们的底层思维,永远都是效率高,不加班的思想,我相信go-zero会提高你和你团队或是公司的效率。go-zero的作者说,他们有个团队专门整理go-zero框架,目的也应该很明显,那就是提高,他们自己的开发效率,流程化,标准化,是提高工作效率的准则,像我们平时遇到了问题,或是遇到了bug,我第一个想到的不是怎么去解决我的bug,而是在想我的流程是不是有问题,我的哪个流程会导致bug,最后我相信 go-zero 能成为 微服务开发 的首选框架。 最后说说遇到的坑吧: grpc grpc 本人第一次用,然后就遇到了,有些字符为空时,字段值不显示的问题: 通过 grpc 官方库中的 jsonpb 来实现,官方在它的设定中有一个结构体用来实现 protoc buffer 转换为JSON结构,并可以根据字段来配置转换的要求。 跨域问题 go-zero 中设置了,感觉没有效果,大佬说通过nginx 设置,后面发现还是不行,最近强行弄到了一个域名下,后面有时间再解决。 sqlx go-zero 的 sqlx 问题,这个真的费了很长的时间: time.Time 这个数据结构,数据库中用的是 timestamp 这个 比如我的字段 是delete_at 默认数库设置的是null ,结果插入的时候,就报了 Incorrect datetime value: '0000-00-00' for column 'deleted_at' at row 1\"} 这个错,查询的时候报 deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" 后面果断去掉了这个字段,字段上面加上 .omitempty 这个标签,好像也有用,db:\".omitempty\" 其次就是这个 Conversion from collation utf8_general_ci into utf8mb4_unicode_ci,这个导致的大概原因是,现在都喜欢用emj表情了,mysql数据识别不了。 数据连接 mysql 这边照样按照原始的方式,将配置文件修改编码格式,重新创建数据库,并且设置数据库编码为utf8mb4,排序规则为 utf8mb4_unicode_ci。 这样的话,所有的表还有string字段都是这个编码格式,如果不想所有的都是,可以单独设置,这个不是重点.因为在navicat上都好设置,手动点一下就行了。 重点来了:golang中使用的是 github.com/go-sql-driver/mysql 驱动,将连接 mysql的 dsn(因为我这使用的是gorm,所以dsn可能跟原生的格式不太一样,不过没关系, 只需要关注 charset 和 collation 就行了) root:password@/name?parseTime=True&loc=Local&charset=utf8 修改为: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"stream.html":{"url":"stream.html","title":"流数据处理利器","keywords":"","body":"流数据处理利器 流处理 (Stream processing) 是一种计算机编程范式,其允许给定一个数据序列 (流处理数据源),一系列数据操作 (函数) 被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到 Kafka,然后再由 Job 消费 Kafaka 写到 elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx go-zero 是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx ,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce 工具进行处理。 总结 本篇文章介绍了流处理的基本概念和go-zero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"online-exchange.html":{"url":"online-exchange.html","title":"10月3日线上交流问题汇总","keywords":"","body":"10月3日线上交流问题汇总 go-zero适用场景 希望说说应用场景,各个场景下的优势 高并发的微服务系统 支撑千万级日活,百万级QPS 完整的微服务治理能力 支持自定义中间件 很好的管理了数据库和缓存 有效隔离故障 低并发的单体系统 这种系统直接使用api层即可,无需rpc服务 各个功能的使用场景以及使用案例 限流 熔断 降载 超时 可观测性 go-zero的实际体验 服务很稳 前后端接口一致性,一个api文件即可生成前后端代码 规范、代码量少,意味着bug少 免除api文档,极大降低沟通成本 代码结构完全一致,便于维护和接手 微服务的项目结构, monorepo的 CICD 处理 bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model mono repo的CI我们是通过gitlab做的,CD使用jenkins CI尽可能更严格的模式,比如-race,使用sonar等工具 CD有开发、测试、预发、灰度和正式集群 晚6点上灰度、无故障的话第二天10点自动同步到正式集群 正式集群分为多个k8s集群,有效的防止单集群故障,直接摘除即可,集群升级更有好 如何部署,如何监控? 全量K8S,通过jenkins自动打包成docker镜像,按照时间打包tag,这样可以一眼看出哪一天的镜像 上面已经讲了,预发->灰度->正式 Prometheus+自建dashboard服务 基于日志检测服务和请求异常 如果打算换go-zero框架重构业务,如何做好线上业务稳定安全用户无感切换?另外咨询下如何进行服务划分? 逐步替换,从外到内,加个proxy来校对,校对一周后可以切换 如有数据库重构,则需要做好新老同步 服务划分按照业务来,遵循从粗到细的原则,避免一个api一个微服务 数据拆分对于微服务来讲尤为重要,上层好拆,数据难拆,尽可能保证按照业务拆分数据 服务发现 服务发现 etcd 的 key 的设计 服务key+时间戳,服务进程数存在时间戳冲突的概率极低,忽略 etcd服务发现与治理, 异常捕获与处理异常 为啥k8s还使用etcd做服务发现,因为dns的刷新有延迟,导致滚动更新会有大量失败,而etcd可以做到完全无损更新 etcd集群直接部署在k8s集群内,因为多个正式集群,集群单点和注册避免混乱 针对etcd异常或者leader切换,自动侦测并刷新,当etcd有异常不能恢复时,不会刷新服务列表,保障服务依然可用 缓存的设计与使用案例 分布式多redis集群,线上最大几十个集群为同一个服务提供缓存服务 无缝扩缩容 不存在没有过期时间的缓存,避免大量不常使用的数据占用资源,默认一周 缓存穿透,没有的数据会短暂缓存一分钟,避免刷接口或大量不存在的数据请求带垮系统 缓存击穿,一个进程只会刷新一次同一个数据,避免热点数据被大量同时加载 缓存雪崩,对缓存过期时间自动做了jitter,5%的标准变差,使得一周的过期时间分布在16小时内,有效防止了雪崩 我们线上数据库都有缓存,否则无法支撑海量并发 自动缓存管理已经内置于go-zero,并可以通过goctl自动生成代码 能否讲解下, 中间件,拦截器的设计思想 洋葱模型 本中间件处理,比如限流,熔断等,然后决定是否调用next next调用 对next调用返回结果做处理 微服务的事务处理怎么实现好,gozero分布式事务设计和实现,有什么好中间件推荐 2PC,两阶段提交 TCC,Try-Confirm-Cancel 消息队列,最大尝试 人工补偿 多级 goroutine 的异常捕获 ,怎么设计比较好 微服务系统请求异常应该隔离,不能让单个异常请求带崩整个进程 go-zero自带了RunSafe/GoSafe,用来防止单个异常请求导致进程崩溃 监控需要跟上,防止异常过量而不自知 fail fast和故障隔离的矛盾点 k8s配置的生成与使用(gateway, service, slb) 内部自动生成k8s的yaml文件,过于依赖配置而未开源 打算在bookstore的示例里加上k8s配置样板 slb->nginx->nodeport->api gateway->rpc service gateway限流、熔断和降载 限流分为两种:并发控制和分布式限流 并发控制用来防止瞬间过量请求,保护系统不被打垮 分布式限流用来给不同服务配置不同的quota 熔断是为了对依赖的服务进行保护,当一个服务出现大量异常的时候,调用者应该给予保护,使其有机会恢复正常,同时也达到fail fast的效果 降载是为了保护当前进程资源耗尽而陷入彻底不可用,确保尽可能服务好能承载的最大请求量 降载配合k8s,可以有效保护k8s扩容,k8s扩容分钟级,go-zero降载秒级 介绍core中好用的组件,如timingwheel等,讲讲设计思路 布隆过滤器 进程内cache RollingWindow TimingWheel 各种executors fx包,map/reduce/filter/sort/group/distinct/head/tail... 一致性hash实现 分布式限流实现 mapreduce,带cancel能力 syncx包里有大量的并发工具 如何快速增加一种rpc协议支持,將跨机发现改为调本机节点,并关闭复杂filter和负载均衡功能 go-zero跟grpc关系还是比较紧密的,设计之初没有考虑支持grpc以外的协议 如果要增加的话,那就只能fork出来魔改了 调本机直接用direct的scheme即可 为啥要去掉filter和负载均衡?如果要去的话,fork了改,但没必要 日志和监控和链路追踪的设计和实现思路,最好有大概图解 日志和监控我们使用prometheus, 自定义dashboard服务,捆绑提交数据(每分钟) 链路追踪可以看出调用关系,自动记录trace日志 go-zero框架有用到什么池化技术吗?如果有,在哪些core代码里面可以参考 一般不需要提前优化,过度优化是大忌 core/syncx/pool.go里面定义了带过期时间的通用池化技术 go-zero用到了那些性能测试方法框架,有代码参考吗?可以说说思路和经验 go benchmark 压测可以通过现有业务日志样本,来按照预估等比放大 压测一定要压到系统扛不住,看第一个瓶颈在哪里,改完再压,循环 说一下代码的抽象经验和心得 Don’t repeat yourself 你未必需要它,之前经常有业务开发人员问我可不可以增加这个功能或那个功能,我一般都会仔细询问深层次目的,很多时候会发现其实这个功能是多余的,不需要才是最佳实践 Martin Fowler提出出现三次再抽象的原则,有时有些同事会找我往框架里增加一个功能,我思考后经常会回答这个你先在业务层写,其它地方也有需要了你再告诉我,三次出现我会考虑集成到框架里 一个文件应该尽量只做一件事,每个文件尽可能控制在200行以内,一个函数尽可能控制在50行以内,这样不需要滚动就可以看到整个函数 需要抽象和提炼的能力,多去思考,经常回头思考之前的架构或实现 你会就go-zero 框架从设计到实践出书吗?框架以后的发展规划是什么? 暂无出书计划,做好框架是最重要的 继续着眼于工程效率 提升服务治理能力 帮助业务开发尽可能快速落地 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"contributor.html":{"url":"contributor.html","title":"贡献人员","keywords":"","body":"社区贡献 作者 kevwan go-zero参与人员 kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy Suyghur heyanfu sjatsh Mikaelemmmm foliet wubenqi fondoger taobig shenbaise9527 reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl zzzfwww HarryWang29 workman-Lu wuqinqiang soasurs weicut almas1992 AlexLast appleboy pipi-lv Howie59 ShyunnY SnakeHacker sohamtembhurne toby1991 masonchen2014 lucaq lchjczw heyehang guonaihong wangzeping722 fyyang kscooo cuishuang bensonfx lovelly lowang-bh magickeha yangwenmai me-cs mlboy mongobaba moyrne ahmczsy lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang yiGmMk fffreedom linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 liyiwu zeromake zzhaolei zzZZzzz888 qwxingzhe sixwaaaay liuqing6767 peasfarmer lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle RivenChan toventang vankillua jsonMark shssen rcyw weibobo windk wojiukankan SeigeC wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 2822132073 l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen ch3nnn cuisongliu dahaihu dylanNew edieruby elza2 codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog 文档贡献人员 kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:51 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"文档贡献","keywords":"","body":"文档贡献 怎么贡献文档? 点击顶部\"编辑此页\"按钮即可进入源码仓库对应的文件,开发人员将修改(添加)的文档通过pr形式提交, 我们收到pr后会进行文档审核,一旦审核通过即可更新文档。 可以贡献哪些文档? 文档编写错误 文档不规范、不完整 go-zero应用实践、心得 组件中心 文档pr通过后文档多久会更新? 在pr接受后,github action会自动build gitbook并发布,因此在github action成功后1-2分钟即可查看更新后的文档。 文档贡献注意事项 纠错、完善源文档可以直接编写原来的md文件 新增组件文档需要保证文档排版、易读,且组件文档需要放在组件中心子目录中 go-zero应用实践分享可以直接放在开发实践子目录下 目录结构规范 目录结构不宜过深,最好不要超过3层 组件文档需要在归属到组件中心,如* [开发实践](practise.md) * [logx](logx.md) * [bloom](bloom.md) * [executors](executors.md) * 你的文档目录名称 应用实践需要归属到开发实践,如* [开发实践](practise.md) * [我是如何用go-zero 实现一个中台系统](datacenter.md) * [流数据处理利器](stream.md) * [10月3日线上交流问题汇总](online-exchange.md * 你的文档目录名称 开发实践文档模板 # 标题 > 作者:填入作者名称 > > 原文连接: 原文连接 some markdown content 猜你想看 怎么参与贡献 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error.html":{"url":"error.html","title":"常见错误处理","keywords":"","body":"常见错误处理 Windows上报错 A required privilege is not held by the client. 解决方法:\"以管理员身份运行\" goctl 即可。 grpc引起错误 错误一 protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. 解决方法: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go安装失败 go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout 请确认GOPROXY已经设置,GOPROXY设置见go module配置 api服务启动失败 error: config file etc/user-api.yaml, error: type mismatch for field xx 请确认user-api.yaml配置文件中配置项是否已经配置,如果有值,检查一下yaml配置文件是否符合yaml格式。 goctl找不到 command not found: goctl 请确保goctl已经安装或者goctl是否已经添加到环境变量 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"source.html":{"url":"source.html","title":"相关源码","keywords":"","body":"相关源码 demo源码 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tips.html":{"url":"tips.html","title":"阅读须知","keywords":"","body":"阅读须知 本文档从快速入门,详细项目开发流程,go-zero服务设计思想,goctl工具的使用等维度进行了介绍, 对于刚刚接触go或go-zero的同学需要把这些篇幅都看完才能有所了解,因此有些费力,这里建议大家阅读的方法。 保持耐心跟着文档目录进行,文档是按照从简单到深入的渐进式过程编写的。 在遇到问题或错误时,请一定记住多查FAQ。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"shorturl.html":{"url":"shorturl.html","title":"快速构建高并发微服务","keywords":"","body":"快速构建高并发微服务 English | 简体中文 0. 为什么说做好微服务很难 要想做好微服务,我们需要理解和掌握的知识点非常多,从几个维度上来说: 基本功能层面 并发控制 & 限流,避免服务被突发流量击垮 服务注册与服务发现,确保能够动态侦测增减的节点 负载均衡,需要根据节点承受能力分发流量 超时控制,避免对已超时请求做无用功 熔断设计,快速失败,保障故障节点的恢复能力 高阶功能层面 请求认证,确保每个用户只能访问自己的数据 链路追踪,用于理解整个系统和快速定位特定请求的问题 日志,用于数据收集和问题定位 可观测性,没有度量就没有优化 对于其中每一点,我们都需要用很长的篇幅来讲述其原理和实现,那么对我们后端开发者来说,要想把这些知识点都掌握并落实到业务系统里,难度是非常大的,不过我们可以依赖已经被大流量验证过的框架体系。go-zero 微服务框架就是为此而生。 另外,我们始终秉承 工具大于约定和文档 的理念。我们希望尽可能减少开发人员的心智负担,把精力都投入到产生业务价值的代码上,减少重复代码的编写,所以我们开发了 goctl 工具。 下面我通过短链微服务来演示通过 go-zero 快速的创建微服务的流程,走完一遍,你就会发现:原来编写微服务如此简单! 1. 什么是短链服务 短链服务就是将长的 URL 网址,通过程序计算等方式,转换为简短的网址字符串。 写此短链服务是为了从整体上演示 go-zero 构建完整微服务的过程,算法和实现细节尽可能简化了,所以这不是一个高阶的短链服务。 2. 短链微服务架构图 这里只用了 Transform RPC 一个微服务,并不是说 API Gateway 只能调用一个微服务,只是为了最简演示 API Gateway 如何调用 RPC 微服务而已 在真正项目里要尽可能每个微服务使用自己的数据库,数据边界要清晰 3. goctl 各层代码生成一览 所有绿色背景的功能模块是自动生成的,按需激活,红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑,各层示意图分别如下: API Gateway RPC model 下面我们来一起完整走一遍快速构建微服务的流程,Let’s Go!🏃‍♂️ 4. 准备工作 安装 etcd, mysql, redis 安装 protoc-gen-go $ go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 安装 protoc $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip $ unzip protoc-3.14.0-linux-x86_64.zip $ mv bin/protoc /usr/local/bin/ 安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 创建工作目录 shorturl 和 shorturl/api mkdir -p shorturl/api 在 shorturl 目录下执行 go mod init shorturl 初始化 go.mod module shorturl go 1.15 require ( github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.4.2 github.com/zeromicro/go-zero v1.3.0 golang.org/x/net v0.0.0-20200707034311-ab3426394381 google.golang.org/grpc v1.29.1 ) 注意:这里可能存在 grpc 版本依赖的问题,可以用以上配置 5. 编写 API Gateway 代码 在 shorturl/api 目录下通过 goctl 生成 api/shorturl.api: $ goctl api -o shorturl.api 编辑 api/shorturl.api,为了简洁,去除了文件开头的 info,代码如下: type ( expandReq { shorten string `form:\"shorten\"` } expandResp { url string `json:\"url\"` } ) type ( shortenReq { url string `form:\"url\"` } shortenResp { shorten string `json:\"shorten\"` } ) service shorturl-api { @server( handler: ShortenHandler ) get /shorten(shortenReq) returns(shortenResp) @server( handler: ExpandHandler ) get /expand(expandReq) returns(expandResp) } type 用法和 go 一致,service 用来定义 get/post/head/delete 等 api 请求,解释如下: service shorturl-api { 这一行定义了 service 名字 @server 部分用来定义 server 端用到的属性 handler 定义了服务端 handler 名字 get /shorten(shortenReq) returns(shortenResp) 定义了 get 方法的路由、请求参数、返回参数等 使用 goctl 生成 API Gateway 代码 $ goctl api go -api shorturl.api -dir . 生成的文件结构如下: . ├── api │ ├── etc │ │ └── shorturl-api.yaml // 配置文件 │ ├── internal │ │ ├── config │ │ │ └── config.go // 定义配置 │ │ ├── handler │ │ │ ├── expandhandler.go // 实现 expandHandler │ │ │ ├── routes.go // 定义路由处理 │ │ │ └── shortenhandler.go // 实现 shortenHandler │ │ ├── logic │ │ │ ├── expandlogic.go // 实现 ExpandLogic │ │ │ └── shortenlogic.go // 实现 ShortenLogic │ │ ├── svc │ │ │ └── servicecontext.go // 定义 ServiceContext │ │ └── types │ │ └── types.go // 定义请求、返回结构体 │ ├── shorturl.api │ └── shorturl.go // main 入口定义 ├── go.mod └── go.sum 启动 API Gateway 服务,默认侦听在 8888 端口 $ go run shorturl.go -f etc/shorturl-api.yaml 测试 API Gateway 服务 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 27 Aug 2020 14:31:39 GMT Content-Length: 15 {\"shorten\":\"\"} 可以看到我们 API Gateway 其实啥也没干,就返回了个空值,接下来我们会在 rpc 服务里实现业务逻辑 可以修改 internal/svc/servicecontext.go 来传递服务依赖(如果需要) 实现逻辑可以修改 internal/logic 下的对应文件 可以通过 goctl 生成各种客户端语言的 api 调用代码 到这里,你已经可以通过 goctl 生成客户端代码给客户端同学并行开发了,支持多种语言,详见文档 6. 编写 transform rpc 服务 在 shorturl 目录下创建 rpc 目录 在 rpc/transform 目录下编写 transform.proto 文件 可以通过命令生成 proto 文件模板 $ goctl rpc template -o transform.proto 修改后文件内容如下: syntax = \"proto3\"; package transform; message expandReq { string shorten = 1; } message expandResp { string url = 1; } message shortenReq { string url = 1; } message shortenResp { string shorten = 1; } service transformer { rpc expand(expandReq) returns(expandResp); rpc shorten(shortenReq) returns(shortenResp); } 用 goctl 生成 rpc 代码,在 rpc/transform 目录下执行命令 $ goctl rpc proto -src transform.proto -dir . 注意:不能在 GOPATH 目录下执行以上命令 文件结构如下: rpc/transform ├── etc │ └── transform.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ ├── expandlogic.go // expand 业务逻辑在这里实现 │ │ └── shortenlogic.go // shorten 业务逻辑在这里实现 │ ├── server │ │ └── transformerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义 ServiceContext,传递依赖 ├── pb │ └── transform.pb.go ├── transform.go // rpc 服务 main 函数 ├── transform.proto └── transformer ├── transformer.go // 提供了外部调用方法,无需修改 ├── transformer_mock.go // mock 方法,测试用 └── types.go // request/response 结构体定义 直接可以运行,如下: $ go run transform.go -f etc/transform.yaml Starting rpc server at 127.0.0.1:8080... 查看服务是否注册 $ ETCDCTL_API=3 etcdctl get transform.rpc --prefix transform.rpc/7587851893787585061 127.0.0.1:8080 etc/transform.yaml 文件里可以修改侦听端口等配置 7. 修改 API Gateway 代码调用 transform rpc 服务 修改配置文件 shorturl-api.yaml,增加如下内容 Transform: Etcd: Hosts: - localhost:2379 Key: transform.rpc 通过 etcd 自动去发现可用的 transform 服务 修改 internal/config/config.go 如下,增加 transform 服务依赖 type Config struct { rest.RestConf Transform zrpc.RpcClientConf // 手动代码 } 修改 internal/svc/servicecontext.go,如下: type ServiceContext struct { Config config.Config Transformer transformer.Transformer // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Transformer: transformer.NewTransformer(zrpc.MustNewClient(c.Transform)), // 手动代码 } } 通过 ServiceContext 在不同业务逻辑之间传递依赖 修改 internal/logic/expandlogic.go 里的 Expand 方法,如下: func (l *ExpandLogic) Expand(req types.ExpandReq) (types.ExpandResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Expand(l.ctx, &transformer.ExpandReq{ Shorten: req.Shorten, }) if err != nil { return types.ExpandResp{}, err } return types.ExpandResp{ Url: resp.Url, }, nil // 手动代码结束 } 通过调用 transformer 的 Expand 方法实现短链恢复到 url 修改 internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(req types.ShortenReq) (types.ShortenResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Shorten(l.ctx, &transformer.ShortenReq{ Url: req.Url, }) if err != nil { return types.ShortenResp{}, err } return types.ShortenResp{ Shorten: resp.Shorten, }, nil // 手动代码结束 } 有的版本生成返回值可能是指针类型,需要自己调整下 通过调用 transformer 的 Shorten 方法实现 url 到短链的变换 至此,API Gateway 修改完成,虽然贴的代码多,但是其中修改的是很少的一部分,为了方便理解上下文,我贴了完整代码,接下来处理 CRUD+cache 8. 定义数据库表结构,并生成 CRUD+cache 代码 shorturl 下创建 rpc/transform/model 目录:mkdir -p rpc/transform/model 在 rpc/transform/model 目录下编写创建 shorturl 表的 sql 文件 shorturl.sql,如下: CREATE TABLE `shorturl` ( `shorten` varchar(255) NOT NULL COMMENT 'shorten key', `url` varchar(255) NOT NULL COMMENT 'original url', PRIMARY KEY(`shorten`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 创建 DB 和 table create database gozero; source shorturl.sql; 在 rpc/transform/model 目录下执行如下命令生成 CRUD+cache 代码,-c 表示使用 redis cache $ goctl model mysql ddl -c -src shorturl.sql -dir . 也可以用 datasource 命令代替 ddl 来指定数据库链接直接从 schema 生成 生成后的文件结构如下: Plain Text rpc/transform/model ├── shorturl.sql ├── shorturlmodel.go // CRUD+cache 代码 └── vars.go // 定义常量和变量 9. 修改 shorten/expand rpc 代码调用 crud+cache 代码 修改 rpc/transform/etc/transform.yaml,增加如下内容: DataSource: root:password@tcp(localhost:3306)/gozero Table: shorturl Cache: - Host: localhost:6379 可以使用多个 redis 作为 cache,支持 redis 单点或者 redis 集群 修改 rpc/transform/internal/config/config.go,如下: type Config struct { zrpc.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 } 增加了 mysql 和 redis cache 配置 修改 rpc/transform/internal/svc/servicecontext.go,如下: type ServiceContext struct { c config.Config Model model.ShorturlModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache), // 手动代码 } } 修改 rpc/transform/internal/logic/expandlogic.go,如下: func (l *ExpandLogic) Expand(in *transform.ExpandReq) (*transform.ExpandResp, error) { // 手动代码开始 res, err := l.svcCtx.Model.FindOne(l.ctx, in.Shorten) if err != nil { return nil, err } return &transform.ExpandResp{ Url: res.Url, }, nil // 手动代码结束 } 修改 rpc/transform/internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(in *transform.ShortenReq) (*transform.ShortenResp, error) { // 手动代码开始,生成短链接 key := hash.Md5Hex([]byte(in.Url))[:6] object, _ := l.svcCtx.Model.FindOne(l.ctx, key) if object != nil { return &transform.ShortenResp{ Shorten: key, }, nil } _, err := l.svcCtx.Model.Insert(l.ctx, &model.Shorturl{ Shorten: key, Url: in.Url, }) if err != nil { return nil, err } return &transform.ShortenResp{ Shorten: key, }, nil // 手动代码结束 } 至此代码修改完成,凡是手动修改的代码我加了标注 注意: undefined cache,你需要 import \"github.com/zeromicro/go-zero/core/stores/cache\" undefined model, sqlx, hash 等,你需要在文件中 import \"shorturl/rpc/transform/model\" import \"github.com/zeromicro/go-zero/core/stores/sqlx\" 10. 完整调用演示 shorten api 调用 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:49:49 GMT Content-Length: 21 {\"shorten\":\"f35b2a\"} expand api 调用 $ curl -i \"http://localhost:8888/expand?shorten=f35b2a\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:51:53 GMT Content-Length: 34 {\"url\":\"http://www.xiaoheiban.cn\"} 11. Benchmark 因为写入依赖于 mysql 的写入速度,就相当于压 mysql 了,所以压测只测试了 expand 接口,相当于从 mysql 里读取并利用缓存,shorten.lua 里随机从 db 里获取了 100 个热 key 来生成压测请求 可以看出在我的 MacBook Pro 上能达到 3 万 + 的 qps。 12. 完整代码 https://github.com/zeromicro/zero-examples/tree/main/shorturl 12. 总结 我们一直强调 工具大于约定和文档。 go-zero 不只是一个框架,更是一个建立在框架 + 工具基础上的,简化和规范了整个微服务构建的技术体系。 我们在保持简单的同时也尽可能把微服务治理的复杂度封装到了框架内部,极大的降低了开发人员的心智负担,使得业务开发得以快速推进。 通过 go-zero+goctl 生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。 有任何好的提升工程效率的想法,随时欢迎交流!👏 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"breaker-algorithms.html":{"url":"breaker-algorithms.html","title":"熔断原理与实现","keywords":"","body":"熔断原理与实现 在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机 由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断 熔断器原理 熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误 在这种模式下,服务调用方为每一个调用服务(调用路径)维护一个状态机,在这个状态机中有三个状态: 关闭(Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态 打开(Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复 半打开(Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到打开状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮 服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回 熔断器引入 上面介绍了熔断器的原理,在了解完原理后,你是否有思考我们如何引入熔断器呢?一种方案是在业务逻辑中可以加入熔断器,但显然是不够优雅也不够通用的,因此我们需要把熔断器集成在框架内,在zRPC框架内就内置了熔断器 我们知道,熔断器主要是用来保护调用端,调用端在发起请求的时候需要先经过熔断器,而客户端拦截器正好兼具了这个这个功能,所以在zRPC框架内熔断器是实现在客户端拦截器内,拦截器的原理如下图: 对应的代码为: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // 基于请求方法进行熔断 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正发起调用 return invoker(ctx, method, req, reply, cc, opts...) // codes.Acceptable判断哪种错误需要加入熔断错误计数 }, codes.Acceptable) } 熔断器实现 zRPC中熔断器的实现参考了Google Sre过载保护算法,该算法的原理如下: 请求数量(requests):调用方发起请求的数量总和 请求接受数量(accepts):被调用方正常处理的请求数量 在正常情况下,这两个值是相等的,随着被调用方服务出现异常开始拒绝请求,请求接受数量(accepts)的值开始逐渐小于请求数量(requests),这个时候调用方可以继续发送请求,直到requests = K * accepts,一旦超过这个限制,熔断器就回打开,新的请求会在本地以一定的概率被抛弃直接返回错误,概率的计算公式如下: 通过修改算法中的K(倍值),可以调节熔断器的敏感度,当降低该倍值会使自适应熔断算法更敏感,当增加该倍值会使得自适应熔断算法降低敏感度,举例来说,假设将调用方的请求上限从 requests = 2 acceptst 调整为 requests = 1.1 accepts 那么就意味着调用方每十个请求之中就有一个请求会触发熔断 代码路径为go-zero/core/breaker type googleBreaker struct { k float64 // 倍值 默认1.5 stat *collection.RollingWindow // 滑动时间窗口,用来对请求失败和成功计数 proba *mathx.Proba // 动态概率 } 自适应熔断算法实现 func (b *googleBreaker) accept() error { accepts, total := b.history() // 请求接受数量和请求总量 weightedAccepts := b.k * float64(accepts) // 计算丢弃请求概率 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio 每次发起请求会调用doReq方法,在这个方法中首先通过accept效验是否触发熔断,acceptable用来判断哪些error会计入失败计数,定义如下: func Acceptable(err error) bool { switch status.Code(err) { case codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss: // 异常请求错误 return false default: return true } } 如果请求正常则通过markSuccess把请求数量和请求接受数量都加一,如果请求不正常则只有请求数量会加一 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { // 判断是否触发熔断 if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 执行真正的调用 err := req() // 正常请求计数 if acceptable(err) { b.markSuccess() } else { // 异常请求计数 b.markFailure() } return err } 总结 调用端可以通过熔断机制进行自我保护,防止调用下游服务出现异常,或者耗时过长影响调用端的业务逻辑,很多功能完整的微服务框架都会内置熔断器。其实,不仅微服务调用之间需要熔断器,在调用依赖资源的时候,比如mysql、redis等也可以引入熔断器的机制。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"collection.html":{"url":"collection.html","title":"进程内缓存组件 collection.Cache","keywords":"","body":"通过 collection.Cache 进行缓存 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 进程内缓存工具collection.Cache 在做服务器开发的时候,相信都会遇到使用缓存的情况,go-zero 提供的简单的缓存封装 collection.Cache,简单使用方式如下 // 初始化 cache,其中 WithLimit 可以指定最大缓存的数量 c, err := collection.NewCache(time.Minute, collection.WithLimit(10000)) if err != nil { panic(err) } // 设置缓存 c.Set(\"key\", user) // 获取缓存,ok:是否存在 v, ok := c.Get(\"key\") // 删除缓存 c.Del(\"key\") // 获取缓存,如果 key 不存在的,则会调用 func 去生成缓存 v, err := c.Take(\"key\", func() (interface{}, error) { return user, nil }) cache 实现的建的功能包括 缓存自动失效,可以指定过期时间 缓存大小限制,可以指定缓存个数 缓存增删改 缓存命中率统计 并发安全 缓存击穿 实现原理: Cache 自动失效,是采用 TimingWheel(https://github.com/zeromicro/zeromicro/blob/master/core/collection/timingwheel.go) 进行管理的 timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) Cache 大小限制,是采用 LRU 淘汰策略,在新增缓存的时候会去检查是否已经超出过限制,具体代码在 keyLru 中实现 func (klru *keyLru) add(key string) { if elem, ok := klru.elements[key]; ok { klru.evicts.MoveToFront(elem) return } // Add new item elem := klru.evicts.PushFront(key) klru.elements[key] = elem // Verify size not exceeded if klru.evicts.Len() > klru.limit { klru.removeOldest() } } Cache 的命中率统计,是在代码中实现 cacheStat,在缓存命中丢失的时候自动统计,并且会定时打印使用的命中率, qps 等状态. 打印的具体效果如下 cache(proc) - qpm: 2, hit_ratio: 50.0%, elements: 0, hit: 1, miss: 1 缓存击穿包含是使用 syncx.SingleFlight(https://github.com/zeromicro/zeromicro/blob/master/core/syncx/singleflight.go) 进行实现的,就是将同时请求同一个 key 的请求, 关于 SingleFlight 后续会继续补充。 相关具体实现是在: func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { v, e := fetch() if e != nil { return nil, e } c.Set(key, v) return v, nil }) if err != nil { return nil, err } if fresh { c.stats.IncrementMiss() return val, nil } else { // got the result from previous ongoing query c.stats.IncrementHit() } return val, nil } 本文主要介绍了go-zero框架中的 Cache 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"keywords.html":{"url":"keywords.html","title":"高效的关键词替换和敏感词过滤工具","keywords":"","body":"高效的关键词替换和敏感词过滤工具 1. 算法介绍 利用高效的Trie树建立关键词树,如下图所示,然后依次查找字符串中的相连字符是否形成树的一条路径 发现掘金上这篇文章写的比较详细,可以一读,具体原理在此不详述。 2. 关键词替换 支持关键词重叠,自动选用最短的关键词,并且只会对原始字符串只会遍历一次进行匹配替换,如果替换结果中又出现的关键词不会被二次替换(如果业务上有这种可能性,请自行对上一次的替换结果再次执行替换操作),代码示例如下: replacer := stringx.NewReplacer(map[string]string{ \"日本\": \"法国\", \"日本的首都\": \"东京\", \"东京\": \"日本的首都\", }) fmt.Println(replacer.Replace(\"日本的首都是东京\")) 可以得到: ```Plain Text 法国的首都是日本的首都 示例代码见`stringx/replace/replace.go` ## 3. 查找敏感词 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }) keywords := filter.FindKeywords(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(keywords) 可以得到: ```Plain Text [苍井空 日本AV女优 AV演员色情 AV AV演员] ## 4. 敏感词过滤 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }, stringx.WithMask('?')) // 默认替换为* safe, keywords, found := filter.Filter(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(safe) fmt.Println(keywords) fmt.Println(found) 可以得到: Plain Text 日本????兼电视、电影演员。?????女优是xx出道, ??????们最精彩的表演是??????表演 [苍井空 日本AV女优 AV演员色情 AV AV演员] true 示例代码见stringx/filter/filter.go 5. Benchmark Sentences Keywords Regex go-zero 10000 10000 16min10s 27.2ms Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"loadshedding.html":{"url":"loadshedding.html","title":"服务自适应降载保护设计","keywords":"","body":"服务自适应降载保护设计 设计目的 保证系统不被过量请求拖垮 在保证系统稳定的前提下,尽可能提供更高的吞吐量 设计考虑因素 如何衡量系统负载 是否处于虚机或容器内,需要读取cgroup相关负载 用1000m表示100%CPU,推荐使用800m表示系统高负载 尽可能小的Overhead,不显著增加RT 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决 机制设计 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定 当CPU负载大于指定值时触发降载保护机制 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time) 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个 满足以下所有条件则拒绝该请求 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动 averageFlying > max(1, QPS*minRT/1e3) averageFlying = MovingAverage(flying) 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值 取flying值的时候,有三种做法: 请求增加后更新一次averageFlying,见图中橙色曲线 请求结束后更新一次averageFlying,见图中绿色曲线 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying 我们使用的是第二种,这样可以更好的防止抖动,如图: QPS = maxPass * bucketsPerSecond maxPass表示每个有效桶里的成功的requests bucketsPerSecond表示每秒有多少个桶 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求 降载的使用 已经在rest和zrpc框架里增加了可选激活配置 CpuThreshold,如果把值设置为大于0的值,则激活该服务的自动降载机制 如果请求被drop,那么错误日志里会有dropreq关键字 参考资料 滑动平均 Sentinel自适应限流 Kratos自适应限流保护 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mapping.html":{"url":"mapping.html","title":"文本序列化和反序列化","keywords":"","body":"文本序列化和反序列化 go-zero针对文本的序列化和反序列化主要在三个地方使用 http api请求体的反序列化 http api返回体的序列化 配置文件的反序列化 本文假定读者已经定义过api文件以及修改过配置文件,如不熟悉,可参照 快速构建高并发微服务 快速构建高并发微服务 1. http api请求体的反序列化 在反序列化的过程中的针对请求数据的数据格式以及数据校验需求,go-zero实现了自己的一套反序列化机制 1.1 数据格式以订单order.api文件为例 type ( createOrderReq struct { token string `path:\"token\"` // 用户token productId string `json:\"productId\"` // 商品ID num int `json:\"num\"` // 商品数量 } createOrderRes struct { success bool `json:\"success\"` // 是否成功 } findOrderReq struct { token string `path:\"token\"` // 用户token page int `form:\"page\"` // 页数 pageSize int8 `form:\"pageSize\"` // 页大小 } findOrderRes struct { orderInfo []orderInfo `json:\"orderInfo\"` // 商品ID } orderInfo struct { productId string `json:\"productId\"` // 商品ID productName string `json:\"productName\"` // 商品名称 num int `json:\"num\"` // 商品数量 } deleteOrderReq struct { id string `path:\"id\"` } deleteOrderRes struct { success bool `json:\"success\"` // 是否成功 } ) service order { @doc( summary: 创建订单 ) @handler CreateOrderHandler post /order/add/:token(createOrderReq) returns(createOrderRes) @doc( summary: 获取订单 ) @handler FindOrderHandler get /order/find/:token(findOrderReq) returns(findOrderRes) @doc( summary: 删除订单 ) @handler: DeleteOrderHandler delete /order/:id(deleteOrderReq) returns(deleteOrderRes) } http api请求体的反序列化的tag有三种: path:http url 路径中参数反序列化 /order/add/1234567会解析出来token为1234567 form:http form表单反序列化,需要 header头添加 Content-Type: multipart/form-data /order/find/1234567?page=1&pageSize=20会解析出来token为1234567,page为1,pageSize为20 json:http request json body反序列化,需要 header头添加 Content-Type: application/json {\"productId\":\"321\",\"num\":1}会解析出来productId为321,num为1 1.2 数据校验以用户user.api文件为例 type ( createUserReq struct { age int8 `json:\"age,default=20,range=(12:100]\"` // 年龄 name string `json:\"name\"` // 名字 alias string `json:\"alias,optional\"` // 别名 sex string `json:\"sex,options=male|female\"` // 性别 avatar string `json:\"avatar,default=default.png\"` // 头像 } createUserRes struct { success bool `json:\"success\"` // 是否成功 } ) service user { @doc( summary: 创建订单 ) @handler CreateUserHandler post /user/add(createUserReq) returns(createUserRes) } 数据校验有很多种方式,包括以下但不限: age:默认不输入为20,输入则取值范围为(12:100],前开后闭 name:必填,不可为空 alias:选填,可为空 sex:必填,取值为male或female avatar:选填,默认为default.png 更多详情参见unmarshaler_test.go 2. http api返回体的序列化 使用官方默认的encoding/json包序列化,在此不再累赘 3. 配置文件的反序列化 配置文件的反序列化和http api请求体的反序列化使用同一套解析规则,可参照http api请求体的反序列化 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mapreduce.html":{"url":"mapreduce.html","title":"并发处理工具 MapReduce","keywords":"","body":"通过MapReduce降低服务响应时间 在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了 WaitGroup 工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错 基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,该工具开箱即用,不需要做什么初始化,我们通过下图看下使用MapReduce和没使用的耗时对比: 相同的依赖,串行处理的话需要200ms,使用MapReduce后的耗时等于所有依赖中最大的耗时为100ms,可见MapReduce可以大大降低服务耗时,而且随着依赖的增加效果就会越明显,减少处理耗时的同时并不会增加服务器压力 并发处理工具MapReduce MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想 go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能 我们通过几个示例来演示MapReduce的用法 MapReduce主要有三个参数,第一个参数为generate用以生产数据,第二个参数为mapper用以对数据进行处理,第三个参数为reducer用以对mapper后的数据做聚合返回,还可以通过opts选项设置并发处理的线程数量 场景一: 某些功能的结果往往需要依赖多个服务,比如商品详情的结果往往会依赖用户服务、库存服务、订单服务等等,一般被依赖的服务都是以rpc的形式对外提供,为了降低依赖的耗时我们往往需要对依赖做并行处理 func productDetail(uid, pid int64) (*ProductDetail, error) { var pd ProductDetail err := mr.Finish(func() (err error) { pd.User, err = userRpc.User(uid) return }, func() (err error) { pd.Store, err = storeRpc.Store(pid) return }, func() (err error) { pd.Order, err = orderRpc.Order(pid) return }) if err != nil { log.Printf(\"product detail error: %v\", err) return nil, err } return &pd, nil } 该示例中返回商品详情依赖了多个服务获取数据,因此做并发的依赖处理,对接口的性能有很大的提升 场景二: 很多时候我们需要对一批数据进行处理,比如对一批用户id,效验每个用户的合法性并且效验过程中有一个出错就认为效验失败,返回的结果为效验合法的用户id func checkLegal(uids []int64) ([]int64, error) { r, err := mr.MapReduce(func(source chan 该示例中,如果check过程出现错误则通过cancel方法结束效验过程,并返回error整个效验过程结束,如果某个uid效验结果为false则最终结果不返回该uid MapReduce使用注意事项 mapper和reducer中都可以调用cancel,参数为error,调用后立即返回,返回结果为nil, error mapper中如果不调用writer.Write则item最终不会被reducer聚合 reducer中如果不调用writer.Wirte则返回结果为nil, ErrReduceNoOutput reducer为单线程,所有mapper出来的结果在这里串行聚合 实现原理分析: MapReduce中首先通过buildSource方法通过执行generate(参数为无缓冲channel)产生数据,并返回无缓冲的channel,mapper会从该channel中读取数据 func buildSource(generate GenerateFunc) chan interface{} { source := make(chan interface{}) go func() { defer close(source) generate(source) }() return source } 在MapReduceWithSource方法中定义了cancel方法,mapper和reducer中都可以调用该方法,调用后主线程收到close信号会立马返回 cancel := once(func(err error) { if err != nil { retErr.Set(err) } else { // 默认的error retErr.Set(ErrCancelWithNil) } drain(source) // 调用close(ouput)主线程收到Done信号,立马返回 finish() }) 在mapperDispatcher方法中调用了executeMappers,executeMappers消费buildSource产生的数据,每一个item都会起一个goroutine单独处理,默认最大并发数为16,可以通过WithWorkers进行设置 var wg sync.WaitGroup defer func() { wg.Wait() // 保证所有的item都处理完成 close(collector) }() pool := make(chan lang.PlaceholderType, workers) writer := newGuardedWriter(collector, done) // 将mapper处理完的数据写入collector for { select { case reducer单goroutine对数mapper写入collector的数据进行处理,如果reducer中没有手动调用writer.Write则最终会执行finish方法对output进行close避免死锁 go func() { defer func() { if r := recover(); r != nil { cancel(fmt.Errorf(\"%v\", r)) } else { finish() } }() reducer(collector, writer, cancel) }() 在该工具包中还提供了许多针对不同业务场景的方法,实现原理与MapReduce大同小异,感兴趣的同学可以查看源码学习 MapReduceVoid 功能和MapReduce类似但没有结果返回只返回error Finish 处理固定数量的依赖,返回error,有一个error立即返回 FinishVoid 和Finish方法功能类似,没有返回值 Map 只做generate和mapper处理,返回channel MapVoid 和Map功能类似,无返回 本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"metric.html":{"url":"metric.html","title":"基于prometheus的微服务指标监控","keywords":"","body":"基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero中目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"sharedcalls.html":{"url":"sharedcalls.html","title":"防止缓存击穿之进程内共享调用","keywords":"","body":"防止缓存击穿之进程内共享调用 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等。 本文主要讲述进程内共享调用神器SharedCalls(v1.2.0改名为SingleFlight ) 使用场景 并发场景下,可能会有多个线程(协程)同时请求同一份资源,如果每个请求都要走一遍资源的请求过程,除了比较低效之外,还会对资源服务造成并发的压力。举一个具体例子,比如缓存失效,多个请求同时到达某服务请求某资源,该资源在缓存中已经失效,此时这些请求会继续访问DB做查询,会引起数据库压力瞬间增大。而使用SharedCalls可以使得同时多个请求只需要发起一次拿结果的调用,其他请求\"坐享其成\",这种设计有效减少了资源服务的并发压力,可以有效防止缓存击穿。 高并发场景下,当某个热点key缓存失效后,多个请求会同时从数据库加载该资源,并保存到缓存,如果不做防范,可能会导致数据库被直接打死。针对这种场景,go-zero框架中已经提供了实现,具体可参看sqlc和mongoc等实现代码。 为了简化演示代码,我们通过多个线程同时去获取一个id来模拟缓存的场景。如下: func main() { const round = 5 var wg sync.WaitGroup barrier := syncx.NewSharedCalls() wg.Add(round) for i := 0; i 运行,打印结果为: 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 可以看出,只要是同一个key上的同时发起的请求,都会共享同一个结果,对获取DB数据进缓存等场景特别有用,可以有效防止缓存击穿。 关键源码分析 SharedCalls interface提供了Do和DoEx两种方法的抽象 // SharedCalls接口提供了Do和DoEx两种方法 type SharedCalls interface { Do(key string, fn func() (interface{}, error)) (interface{}, error) DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error) } SharedCalls interface的具体实现sharedGroup // call代表对指定资源的一次请求 type call struct { wg sync.WaitGroup // 用于协调各个请求goroutine之间的资源共享 val interface{} // 用于保存请求的返回值 err error // 用于保存请求过程中发生的错误 } type sharedGroup struct { calls map[string]*call lock sync.Mutex } sharedGroup的Do方法 key参数:可以理解为资源的唯一标识。 fn参数:真正获取资源的方法。 处理过程分析: // 当多个请求同时使用Do方法请求资源时 func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) { // 先申请加锁 g.lock.Lock() // 根据key,获取对应的call结果,并用变量c保存 if c, ok := g.calls[key]; ok { // 拿到call以后,释放锁,此处call可能还没有实际数据,只是一个空的内存占位 g.lock.Unlock() // 调用wg.Wait,判断是否有其他goroutine正在申请资源,如果阻塞,说明有其他goroutine正在获取资源 c.wg.Wait() // 当wg.Wait不再阻塞,表示资源获取已经结束,可以直接返回结果 return c.val, c.err } // 没有拿到结果,则调用makeCall方法去获取资源,注意此处仍然是锁住的,可以保证只有一个goroutine可以调用makecall c := g.makeCall(key, fn) // 返回调用结果 return c.val, c.err } sharedGroup的DoEx方法 和Do方法类似,只是返回值中增加了布尔值表示值是调用makeCall方法直接获取的,还是取的共享成果 func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) { g.lock.Lock() if c, ok := g.calls[key]; ok { g.lock.Unlock() c.wg.Wait() return c.val, false, c.err } c := g.makeCall(key, fn) return c.val, true, c.err } sharedGroup的makeCall方法 该方法由Do和DoEx方法调用,是真正发起资源请求的方法。 // 进入makeCall的一定只有一个goroutine,因为要拿锁锁住的 func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call { // 创建call结构,用于保存本次请求的结果 c := new(call) // wg加1,用于通知其他请求资源的goroutine等待本次资源获取的结束 c.wg.Add(1) // 将用于保存结果的call放入map中,以供其他goroutine获取 g.calls[key] = c // 释放锁,这样其他请求的goroutine才能获取call的内存占位 g.lock.Unlock() defer func() { // delete key first, done later. can't reverse the order, because if reverse, // another Do call might wg.Wait() without get notified with wg.Done() g.lock.Lock() delete(g.calls, key) g.lock.Unlock() // 调用wg.Done,通知其他goroutine可以返回结果,这样本批次所有请求完成结果的共享 c.wg.Done() }() // 调用fn方法,将结果填入变量c中 c.val, c.err = fn() return c } 最后 本文主要介绍了go-zero框架中的 SharedCalls工具,对其应用场景和关键代码做了简单的梳理,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"sql-cache.html":{"url":"sql-cache.html","title":"DB缓存机制","keywords":"","body":"DB缓存机制 QueryRowIndex 没有查询条件到Primary映射的缓存 通过查询条件到DB去查询行记录,然后 把Primary到行记录的缓存写到redis里 把查询条件到Primary的映射保存到redis里,框架的Take方法自动做了 可能的过期顺序 查询条件到Primary的映射缓存未过期 Primary到行记录的缓存未过期 直接返回缓存行记录 Primary到行记录的缓存已过期 通过Primary到DB获取行记录,并写入缓存 此时存在的问题是,查询条件到Primary的缓存可能已经快要过期了,短时间内的查询又会触发一次数据库查询 要避免这个问题,可以让上面粗体部分第一个过期时间略长于第二个,比如5秒 查询条件到Primary的映射缓存已过期,不管Primary到行记录的缓存是否过期 查询条件到Primary的映射会被重新获取,获取过程中会自动写入新的Primary到行记录的缓存,这样两种缓存的过期时间都是刚刚设置 有查询条件到Primary映射的缓存 没有Primary到行记录的缓存 通过Primary到DB查询行记录,并写入缓存 有Primary到行记录的缓存 直接返回缓存结果 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"zrpc.html":{"url":"zrpc.html","title":"zrpc 使用介绍","keywords":"","body":"企业级RPC框架zRPC 近期比较火的开源项目go-zero是一个集成了各种工程实践的包含了Web和RPC协议的功能完善的微服务框架,今天我们就一起来分析一下其中的RPC部分zRPC。 zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。 zRPC初探 zRPC支持直连和基于etcd服务发现两种方式,我们以基于etcd做服务发现为例演示zRPC的基本使用: 配置 创建hello.yaml配置文件,配置如下: Name: hello.rpc // 服务名 ListenOn: 127.0.0.1:9090 // 服务监听地址 Etcd: Hosts: - 127.0.0.1:2379 // etcd服务地址 Key: hello.rpc // 服务注册key 创建proto文件 创建hello.proto文件,并生成对应的go代码 syntax = \"proto3\"; package pb; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 生成go代码 protoc --go_out=plugins=grpc:. hello.proto Server端 package main import ( \"context\" \"flag\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type Config struct { zrpc.RpcServerConf } var cfgFile = flag.String(\"f\", \"./hello.yaml\", \"cfg file\") func main() { flag.Parse() var cfg Config conf.MustLoad(*cfgFile, &cfg) srv, err := zrpc.NewServer(cfg.RpcServerConf, func(s *grpc.Server) { pb.RegisterGreeterServer(s, &Hello{}) }) if err != nil { log.Fatal(err) } srv.Start() } type Hello struct{} func (h *Hello) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: \"hello \" + in.Name}, nil } Client端 package main import ( \"context\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/discov\" \"github.com/zeromicro/go-zero/zrpc\" ) func main() { client := zrpc.MustNewClient(zrpc.RpcClientConf{ Etcd: discov.EtcdConf{ Hosts: []string{\"127.0.0.1:2379\"}, Key: \"hello.rpc\", }, }) conn := client.Conn() hello := pb.NewGreeterClient(conn) reply, err := hello.SayHello(context.Background(), &pb.HelloRequest{Name: \"go-zero\"}) if err != nil { log.Fatal(err) } log.Println(reply.Message) } 启动服务,查看服务是否注册: ETCDCTL_API=3 etcdctl get hello.rpc --prefix 显示服务已经注册: hello.rpc/7587849401504590084 127.0.0.1:9090 运行客户端即可看到输出: hello go-zero 这个例子演示了zRPC的基本使用,可以看到通过zRPC构建RPC服务非常简单,只需要很少的几行代码,接下来我们继续进行探索 zRPC原理分析 下图展示zRPC的架构图和主要组成部分 zRPC主要有以下几个模块组成: discov: 服务发现模块,基于etcd实现服务发现功能 resolver: 服务注册模块,实现了gRPC的resolver.Builder接口并注册到gRPC interceptor: 拦截器,对请求和响应进行拦截处理 balancer: 负载均衡模块,实现了p2c负载均衡算法,并注册到gRPC client: zRPC客户端,负责发起请求 server: zRPC服务端,负责处理请求 这里介绍了zRPC的主要组成模块和每个模块的主要功能,其中resolver和balancer模块实现了gRPC开放的接口,实现了自定义的resolver和balancer,拦截器模块是整个zRPC的功能重点,自适应降载、自适应熔断、prometheus服务指标收集等功能都在这里实现 Interceptor模块 gRPC提供了拦截器功能,主要是对请求前后进行额外处理的拦截操作,其中拦截器包含客户端拦截器和服务端拦截器,又分为一元(Unary)拦截器和流(Stream)拦截器,这里我们主要讲解一元拦截器,流拦截器同理。 客户端拦截器定义如下: type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error 其中method为方法名,req,reply分别为请求和响应参数,cc为客户端连接对象,invoker参数是真正执行rpc方法的handler其实在拦截器中被调用执行 服务端拦截器定义如下: type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) 其中req为请求参数,info中包含了请求方法属性,handler为对server端方法的包装,也是在拦截器中被调用执行 zRPC中内置了丰富的拦截器,其中包括自适应降载、自适应熔断、权限验证、prometheus指标收集等等,由于拦截器较多,篇幅有限没法所有的拦截器给大家一一解析,这里我们主要分析两个,自适应熔断和prometheus服务监控指标收集: 内置拦截器分析 自适应熔断(breaker) 当客户端向服务端发起请求,客户端会记录服务端返回的错误,当错误达到一定的比例,客户端会自行的进行熔断处理,丢弃掉一定比例的请求以保护下游依赖,且可以自动恢复。zRPC中自适应熔断遵循《Google SRE》中过载保护策略,算法如下: requests: 总请求数量 accepts: 正常请求数量 K: 倍值 (Google SRE推荐值为2) 可以通过修改K的值来修改熔断发生的激进程度,降低K的值会使得自适应熔断算法更加激进,增加K的值则自适应熔断算法变得不再那么激进 熔断拦截器定义如下: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // target + 方法名 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正执行调用 return invoker(ctx, method, req, reply, cc, opts...) }, codes.Acceptable) } accept方法实现了Google SRE过载保护算法,判断否进行熔断 func (b *googleBreaker) accept() error { // accepts为正常请求数,total为总请求数 accepts, total := b.history() weightedAccepts := b.k * float64(accepts) // 算法实现 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio doReq方法首先判断是否熔断,满足条件直接返回error(circuit breaker is open),不满足条件则对请求数进行累加 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 此处执行RPC请求 err := req() // 正常请求total和accepts都会加1 if acceptable(err) { b.markSuccess() } else { // 请求失败只有total会加1 b.markFailure() } return err } prometheus指标收集 服务监控是了解服务当前运行状态以及变化趋势的重要手段,监控依赖于服务指标的收集,通过prometheus进行监控指标的收集是业界主流方案,zRPC中也采用了prometheus来进行指标的收集 prometheus拦截器定义如下: 这个拦截器主要是对服务的监控指标进行收集,这里主要是对RPC方法的耗时和调用错误进行收集,这里主要使用了Prometheus的Histogram和Counter数据类型 func UnaryPrometheusInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) ( interface{}, error) { // 执行前记录一个时间 startTime := timex.Now() resp, err := handler(ctx, req) // 执行后通过Since算出执行该调用的耗时 metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), info.FullMethod) // 方法对应的错误码 metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err)))) return resp, err } } 添加自定义拦截器 除了内置了丰富的拦截器之外,zRPC同时支持添加自定义拦截器 Client端通过AddInterceptor方法添加一元拦截器: func (rc *RpcClient) AddInterceptor(interceptor grpc.UnaryClientInterceptor) { rc.client.AddInterceptor(interceptor) } Server端通过AddUnaryInterceptors方法添加一元拦截器: func (rs *RpcServer) AddUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) { rs.server.AddUnaryInterceptors(interceptors...) } resolver模块 zRPC服务注册架构图: zRPC中自定义了resolver模块,用来实现服务的注册功能。zRPC底层依赖gRPC,在gRPC中要想自定义resolver需要实现resolver.Builder接口: type Builder interface { Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error) Scheme() string } 其中Build方法返回Resolver,Resolver定义如下: type Resolver interface { ResolveNow(ResolveNowOptions) Close() } 在zRPC中定义了两种resolver,direct和discov,这里我们主要分析基于etcd做服务发现的discov,自定义的resolver需要通过gRPC提供了Register方法进行注册代码如下: func RegisterResolver() { resolver.Register(&dirBuilder) resolver.Register(&disBuilder) } 当我们启动我们的zRPC Server的时候,调用Start方法,会像etcd中注册对应的服务地址: func (ags keepAliveServer) Start(fn RegisterFn) error { // 注册服务地址 if err := ags.registerEtcd(); err != nil { return err } // 启动服务 return ags.Server.Start(fn) } 当我们启动zRPC客户端的时候,在gRPC内部会调用我们自定义resolver的Build方法,zRPC通过在Build方法内调用执行了resolver.ClientConn的UpdateState方法,该方法会把服务地址注册到gRPC客户端内部: func (d *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) ( resolver.Resolver, error) { hosts := strings.FieldsFunc(target.Authority, func(r rune) bool { return r == EndpointSepChar }) // 服务发现 sub, err := discov.NewSubscriber(hosts, target.Endpoint) if err != nil { return nil, err } update := func() { var addrs []resolver.Address for _, val := range subset(sub.Values(), subsetSize) { addrs = append(addrs, resolver.Address{ Addr: val, }) } // 向gRPC注册服务地址 cc.UpdateState(resolver.State{ Addresses: addrs, }) } // 监听 sub.AddListener(update) update() // 返回自定义的resolver.Resolver return &nopResolver{cc: cc}, nil } 在discov中,通过调用load方法从etcd中获取指定服务的所有地址: func (c *cluster) load(cli EtcdClient, key string) { var resp *clientv3.GetResponse for { var err error ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout) // 从etcd中获取指定服务的所有地址 resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix()) cancel() if err == nil { break } logx.Error(err) time.Sleep(coolDownInterval) } var kvs []KV c.lock.Lock() for _, ev := range resp.Kvs { kvs = append(kvs, KV{ Key: string(ev.Key), Val: string(ev.Value), }) } c.lock.Unlock() c.handleChanges(key, kvs) } 并通过watch监听服务地址的变化: func (c *cluster) watch(cli EtcdClient, key string) { rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix()) for { select { case wresp, ok := 这部分主要介绍了zRPC中是如何自定义的resolver,以及基于etcd的服务发现原理,通过这部分的介绍大家可以了解到zRPC内部服务注册发现的原理,源代码比较多只是粗略的从整个流程上进行了分析,如果大家对zRPC的源码比较感兴趣可以自行进行学习 balancer模块 负载均衡原理图: 避免过载是负载均衡策略的一个重要指标,好的负载均衡算法能很好的平衡服务端资源。常用的负载均衡算法有轮训、随机、Hash、加权轮训等。但为了应对各种复杂的场景,简单的负载均衡算法往往表现的不够好,比如轮训算法当服务响应时间变长就很容易导致负载不再平衡, 因此zRPC中自定义了默认负载均衡算法P2C(Power of Two Choices),和resolver类似,要想自定义balancer也需要实现gRPC定义的balancer.Builder接口,由于和resolver类似这里不再带大家一起分析如何自定义balancer,感兴趣的朋友可以查看gRPC相关的文档来进行学习 注意,zRPC是在客户端进行负载均衡,常见的还有通过nginx中间代理的方式 zRPC框架中默认的负载均衡算法为P2C,该算法的主要思想是: 从可用节点列表中做两次随机选择操作,得到节点A、B 比较A、B两个节点,选出负载最低的节点作为被选中的节点 伪代码如下: 主要算法逻辑在Pick方法中实现: func (p *p2cPicker) Pick(ctx context.Context, info balancer.PickInfo) ( conn balancer.SubConn, done func(balancer.DoneInfo), err error) { p.lock.Lock() defer p.lock.Unlock() var chosen *subConn switch len(p.conns) { case 0: return nil, nil, balancer.ErrNoSubConnAvailable case 1: chosen = p.choose(p.conns[0], nil) case 2: chosen = p.choose(p.conns[0], p.conns[1]) default: var node1, node2 *subConn for i := 0; i = a { b++ } // 随机获取所有节点中的两个节点 node1 = p.conns[a] node2 = p.conns[b] // 效验节点是否健康 if node1.healthy() && node2.healthy() { break } } // 选择其中一个节点 chosen = p.choose(node1, node2) } atomic.AddInt64(&chosen.inflight, 1) atomic.AddInt64(&chosen.requests, 1) return chosen.conn, p.buildDoneFunc(chosen), nil } choose方法对随机选择出来的节点进行负载比较从而最终确定选择哪个节点 func (p *p2cPicker) choose(c1, c2 *subConn) *subConn { start := int64(timex.Now()) if c2 == nil { atomic.StoreInt64(&c1.pick, start) return c1 } if c1.load() > c2.load() { c1, c2 = c2, c1 } pick := atomic.LoadInt64(&c2.pick) if start-pick > forcePick && atomic.CompareAndSwapInt64(&c2.pick, pick, start) { return c2 } else { atomic.StoreInt64(&c1.pick, start) return c1 } } 上面主要介绍了zRPC默认负载均衡算法的设计思想和代码实现,那自定义的balancer是如何注册到gRPC的呢,resolver提供了Register方法来进行注册,同样balancer也提供了Register方法来进行注册: func init() { balancer.Register(newBuilder()) } func newBuilder() balancer.Builder { return base.NewBalancerBuilder(Name, new(p2cPickerBuilder)) } 注册balancer之后gRPC怎么知道使用哪个balancer呢?这里我们需要使用配置项进行配置,在NewClient的时候通过grpc.WithBalancerName方法进行配置: func NewClient(target string, opts ...ClientOption) (*client, error) { var cli client opts = append(opts, WithDialOption(grpc.WithBalancerName(p2c.Name))) if err := cli.dial(target, opts...); err != nil { return nil, err } return &cli, nil } 这部分主要介绍了zRPC中内中的负载均衡算法的实现原理以及具体的实现方式,之后介绍了zRPC是如何注册自定义的balancer以及如何选择自定义的balancer,通过这部分大家应该对负载均衡有了更进一步的认识 总结 首先,介绍了zRPC的基本使用方法,可以看到zRPC使用非常简单,只需要少数几行代码就可以构建高性能和自带服务治理能力的RPC服务,当然这里没有面面俱到的介绍zRPC的基本使用,大家可以查看相关文档进行学习 接着,介绍了zRPC的几个重要组成模块以及其实现原理,并分析了部分源码。拦截器模块是整个zRPC的重点,其中内置了丰富的功能,像熔断、监控、降载等等也是构建高可用微服务必不可少的。resolver和balancer模块自定义了gRPC的resolver和balancer,通过该部分可以了解到整个服务注册与发现的原理以及如何构建自己的服务发现系统,同时自定义负载均衡算法也变得不再神秘 最后,zRPC是一个经历过各种工程实践的RPC框架,不论是想要用于生产还是学习其中的设计模式都是一个不可多得的开源项目。希望通过这篇文章的介绍大家能够进一步了解zRPC Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-looklook.html":{"url":"go-zero-looklook.html","title":"使用go-zero开发一个旅游系统go-zero-looklook","keywords":"","body":"使用go-zero开发一个旅游系统go-zero-looklook 因为大家都在说目前go-zero没有一个完整的项目例子,本人接触go-zero可能比较早,go-zero在大约1000start左右我就在用了,后来跟go-zero作者加了微信也熟悉了,go-zero作者非常热心以及耐心的帮我解答了很多问题,我也想积极帮助go-zero推广社区,基本是在社区群内回答大家相关的问题,因为在这个过程中发现很多人觉得go-zero没有一个完整的项目例子,作为想推动社区的一员,索性我就把内部项目删减了一些关键东西,就搞了个可用版本开源出来,主要技术栈包含如下: go-zero nginx网关 filebeat kafka go-stash elasticsearch kibana prometheus grafana jaeger go-queue asynq asynqmon dtm docker docker-compose mysql redis 项目地址 https://github.com/Mikaelemmmm/go-zero-looklook 项目文档 https://github.com/Mikaelemmmm/go-zero-looklook/tree/main/doc 项目简介 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中间件,所用到的技术栈基本是go-zero项目组的自研组件,基本是go-zero全家桶了 另外,前端是小程序,本项目已经对接好了小程序授权登录 以及 微信支付了 ,前端看看后面是否能开源吧 项目目录结构如下: app:所有业务代码包含api、rpc以及mq(消息队列、延迟队列、定时任务) common:通用组件 error、middleware、interceptor、tool、ctxdata等 data:该项目包含该目录依赖所有中间件(mysql、es、redis、grafana等)产生的数据,此目录下的所有内容应该在git忽略文件中,不需要提交。 deploy: filebeat: docker部署filebeat配置 go-stash:go-stash配置 nginx: nginx网关配置 prometheus : prometheus配置 script: gencode:生成api、rpc,以及创建kafka语句,复制粘贴使用 mysql:生成model的sh工具 goctl: 该项目goctl的template,goctl生成自定义代码模版,tempalte用法可参考go-zero文档,复制到家目录下.goctl即可, 该项目用到goctl版本是v1.2.3 doc : 该项目系列文档 系统架构图 业务架构图 网关 nginx做网关,使用nginx的auth模块,调用后端的identity服务统一鉴权,业务内部不鉴权,如果涉及到业务资金比较多也可以在业务中进行二次鉴权,为了安全嘛。 另外,很多同学觉得nginx做网关不太好,这块原理基本一样,可以自行替换成apisix、kong等 开发模式 本项目使用的是微服务开发,api (http) + rpc(grpc) , api充当聚合服务,复杂、涉及到其他业务调用的统一写在rpc中,如果一些不会被其他服务依赖使用的简单业务,可以直接写在api的logic中 日志 关于日志,统一使用filebeat收集,上报到kafka中,由于logstash懂得都懂,资源占用太夸张了,这里使用了go-stash替换了logstash 链接:https://github.com/kevwan/go-stash , go-stash是由go-zero开发团队开发的,性能很高不占资源,主要代码量没多少,只需要配置就可以使用,很简单。它是吧kafka数据源同步到elasticsearch中,默认不支持elasticsearch账号密码,我fork了一份修改了一下,很简单支持了账号、密码 监控 监控采用prometheus,这个go-zero原生支持,只需要配置就可以了,这里可以看项目中的配置 链路追踪 go-zero默认jaeger、zipkin支持,只需要配置就可以了,可以看配置 消息队列 消息队列使用的是go-zero开发团队开发的go-queue,链接:https://github.com/zeromicro/go-queue 这里使用可kq,kq是基于kafka做的高性能消息队列 通用go-queue中也有dq,是延迟队列,不过当前项目没有使用dq 延迟队列、定时任务 延迟队列、定时任务本项目使用的是asynq , google团队给予redis开发的简单中间件, 当然了asynq也支持消息队列,你也可也把kq消息队列替换成这个,毕竟只需要redis不需要在去维护一个kafka也是不错的 链接:https://github.com/hibiken/asynq 分布式事务 分布式事务准备使用的是dtm, 嗯 ,很舒服,之前我写过一篇 \"go-zero对接分布式事务dtm保姆式教程\" 链接地址:https://github.com/Mikaelemmmm/gozerodtm , 本项目目前还未使用到,后续准备直接集成就好了,如果读者使用直接去看那个源码就行了 部署 部署的话,目前这个直接使用docker可以部署整套技术栈,如果上k8s的话 ,最简单直接用阿里云的吧 我说下思路,这个后续会出一个基于阿里云效的部署到k8s文档教程,自己搭建一个gitlab、jenkins、harbor去做的话太费时间了 1、将代码放在阿里云效(当然你整到gitlab也行) 2、在阿里云效创建流水线,基本是一个服务一个流水线了 3、流水线步骤 : ​ 拉取代码--->ci检测(这里可以省略哈,自己看着办)--->构建镜像(go-zero官方有Dockerfile还有教程,别告诉我不会)-->推送到阿里云镜像服务--->使用kubectl去阿里云k8s拉取镜像(ack、ask都行,ask无法使用daemonset 不能用filebeat)---->ok了 另外, 如果你想自己基于gitlab、jenkins、harbor去做的话,嗯 自己去找运维弄吧,我之前也写过一个教程,有空在整吧老哥们!! Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"faq.html":{"url":"faq.html","title":"FAQ","keywords":"","body":"常见问题集合 goctl安装了执行命令却提示 command not found: goctl 字样。 如果你通过 go get 方式安装,那么 goctl 应该位于 $GOPATH 中, 你可以通过 go env GOPATH 查看完整路径,不管你的 goctl 是在 $GOPATH中, 还是在其他目录,出现上述问题的原因就是 goctl 所在目录不在 PATH (环境变量)中所致。 rpc怎么调用 该问题可以参考快速开始中的rpc编写与调用介绍,其中有rpc调用的使用逻辑。 proto使用了import,goctl命令需要怎么写。 goctl 对于import的proto指定 BasePath 提供了 protoc 的flag映射,即 --proto_path, -I, goctl 会将此flag值传递给 protoc. 假设 base.proto 的被main proto 引入了,为什么不生能生成base.pb.go。 对于 base.proto 这种类型的文件,一般都是开发者有message复用的需求,他的来源不止有开发者自己编写的proto文件, 还有可能来源于 google.golang.org/grpc 中提供的一些基本的proto,比如 google/protobuf/any.proto, 如果由 goctl 来生成,那么就失去了集中管理这些proto的意义。 model怎么控制缓存时间 在 sqlc.NewNodeConn 的时候可以通过可选参数 cache.WithExpiry 传递,如缓存时间控制为1天,代码如下: sqlc.NewNodeConn(conn,redis,cache.WithExpiry(24*time.Hour)) jwt鉴权怎么实现 请参考jwt鉴权 api中间件怎么使用 请参考中间件 怎么关闭输出的统计日志(stat)? logx.DisableStat() rpc直连与服务发现连接模式写法 // mode1: 集群直连 // conf:=zrpc.NewDirectClientConf([]string{\"ip:port\"},\"app\",\"token\") // mode2: etcd 服务发现 // conf:=zrpc.NewEtcdClientConf([]string{\"ip:port\"},\"key\",\"app\",\"token\") // client, _ := zrpc.NewClient(conf) // mode3: ip直连mode // client, _ := zrpc.NewClientWithTarget(\"127.0.0.1:8888\") grpc 客户端设置消息大小限制 修改grpc消息大小限制,需要 服务端 和 客户端 都设置 // 需要 grpc 方法里面添加配置项:grpc.MaxCallRecvMsgSize(bytes int)、grpc.MaxCallSendMsgSize(bytes int) // 示例如下:设置 UserRpc.List 列表消息大小限制为 8MB // l.svcCtx.UserRpc.List(l.ctx, &listReq, grpc.MaxCallRecvMsgSize(1024*1024*8), grpc.MaxCallSendMsgSize(1024*1024*8)) faq会不定期更新大家遇到的问题,也欢迎大家把常见问题通过pr写在这里。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "}} \ No newline at end of file diff --git a/cn/service-deployment.html b/cn/service-deployment.html index 7b8757cf4..4869b1d5d 100644 --- a/cn/service-deployment.html +++ b/cn/service-deployment.html @@ -1733,7 +1733,7 @@

    猜你想看

  • etcd文档说明
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1778,7 +1778,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"服务部署","level":"1.8.7","depth":2,"next":{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"previous":{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-deployment.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"服务部署","level":"1.8.7","depth":2,"next":{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"previous":{"title":"CI/CD","level":"1.8.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-deployment.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/service-design.html b/cn/service-design.html index 307cff303..f4c52a40d 100644 --- a/cn/service-design.html +++ b/cn/service-design.html @@ -1591,7 +1591,7 @@

    猜你想看

  • api目录结构介绍
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1636,7 +1636,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"目录拆分","level":"1.8.5.1","depth":3,"next":{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"previous":{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-design.md","mtime":"2024-03-01T16:09:17.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"目录拆分","level":"1.8.5.1","depth":3,"next":{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"previous":{"title":"业务开发","level":"1.8.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"目录拆分","level":"1.8.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"model生成","level":"1.8.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"api文件编写","level":"1.8.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"业务编码","level":"1.8.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"jwt鉴权","level":"1.8.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"中间件使用","level":"1.8.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"rpc服务编写与调用","level":"1.8.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"错误处理","level":"1.8.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-design.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/service-monitor.html b/cn/service-monitor.html index d9bd51692..cbc555ebd 100644 --- a/cn/service-monitor.html +++ b/cn/service-monitor.html @@ -1591,7 +1591,7 @@

    grafana可视

    总结

    以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1636,7 +1636,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"服务监控","level":"1.8.10","depth":2,"next":{"title":"Goctl","level":"1.9","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"previous":{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-monitor.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"服务监控","level":"1.8.10","depth":2,"next":{"title":"Goctl","level":"1.9","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"自动补全设置","level":"1.9.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"命令大全","level":"1.9.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"api命令","level":"1.9.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"rpc命令","level":"1.9.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"model命令","level":"1.9.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"plugin命令","level":"1.9.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"previous":{"title":"链路追踪","level":"1.8.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-monitor.md","mtime":"2024-03-02T02:15:50.578Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/sharedcalls.html b/cn/sharedcalls.html index 4e7b83827..75629f9f1 100644 --- a/cn/sharedcalls.html +++ b/cn/sharedcalls.html @@ -1656,7 +1656,7 @@

    关键۩

    最后

    本文主要介绍了go-zero框架中的 SharedCalls工具,对其应用场景和关键代码做了简单的梳理,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1701,7 +1701,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"next":{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},"previous":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"sharedcalls.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"next":{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},"previous":{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"sharedcalls.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/shorturl.html b/cn/shorturl.html index 0dbaa39c2..dbc3671b1 100644 --- a/cn/shorturl.html +++ b/cn/shorturl.html @@ -1996,7 +1996,7 @@

    12. 总结

    通过 go-zero+goctl 生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。

    有任何好的提升工程效率的想法,随时欢迎交流!👏

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -2041,7 +2041,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"next":{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"previous":{"title":"扩展阅读","level":"1.11","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"shorturl.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"next":{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"previous":{"title":"扩展阅读","level":"1.11","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"shorturl.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/source.html b/cn/source.html index 4bec51704..433b17b0c 100644 --- a/cn/source.html +++ b/cn/source.html @@ -1512,7 +1512,7 @@

    相关源码

  • demo源码
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1553,7 +1553,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"相关源码","level":"1.18","depth":1,"previous":{"title":"FAQ","level":"1.17","depth":1,"path":"faq.md","ref":"faq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"source.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"相关源码","level":"1.18","depth":1,"previous":{"title":"FAQ","level":"1.17","depth":1,"path":"faq.md","ref":"faq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"source.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/sql-cache.html b/cn/sql-cache.html index 7a778589c..406d4e23a 100644 --- a/cn/sql-cache.html +++ b/cn/sql-cache.html @@ -1555,7 +1555,7 @@

    QueryRowIndex

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1600,7 +1600,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"DB缓存机制","level":"1.11.19","depth":2,"next":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},"previous":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"sql-cache.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"DB缓存机制","level":"1.11.19","depth":2,"next":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},"previous":{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"sql-cache.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/stream.html b/cn/stream.html index 6b18c2bc0..0ad101195 100644 --- a/cn/stream.html +++ b/cn/stream.html @@ -1843,7 +1843,7 @@

    并发处理

    总结

    本篇文章介绍了流处理的基本概念和go-zero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1888,7 +1888,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"流数据处理利器","level":"1.11.26","depth":2,"next":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"previous":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"stream.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"流数据处理利器","level":"1.11.26","depth":2,"next":{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"previous":{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"stream.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/template-cmd.html b/cn/template-cmd.html index 9df737f5e..d88f47dd7 100644 --- a/cn/template-cmd.html +++ b/cn/template-cmd.html @@ -1606,7 +1606,7 @@

    使用示例

    Done
     
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1651,7 +1651,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"模板操作","level":"1.10.1","depth":2,"next":{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"previous":{"title":"模板管理","level":"1.10","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-cmd.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"模板操作","level":"1.10.1","depth":2,"next":{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"previous":{"title":"模板管理","level":"1.10","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"自定义模板","level":"1.10.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-cmd.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/template-manage.html b/cn/template-manage.html index 76c5ff510..76e8a0889 100644 --- a/cn/template-manage.html +++ b/cn/template-manage.html @@ -1515,7 +1515,7 @@

    模板管理

  • 自定义模板
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1560,7 +1560,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"模板管理","level":"1.10","depth":1,"next":{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"previous":{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-manage.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"模板管理","level":"1.10","depth":1,"next":{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"previous":{"title":"其他命令","level":"1.9.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-manage.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/template.html b/cn/template.html index 04d062776..8d5c9262b 100644 --- a/cn/template.html +++ b/cn/template.html @@ -1662,7 +1662,7 @@

    总结

    ...
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1707,7 +1707,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"自定义模板","level":"1.10.2","depth":2,"next":{"title":"扩展阅读","level":"1.11","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"previous":{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"自定义模板","level":"1.10.2","depth":2,"next":{"title":"扩展阅读","level":"1.11","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"快速构建高并发微服务","level":"1.11.1","depth":2,"path":"shorturl.md","ref":"shorturl.md","articles":[]},{"title":"日志组件介绍","level":"1.11.2","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"布隆过滤器","level":"1.11.3","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.11.4","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"流处理组件 fx","level":"1.11.5","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"go-zero mysql使用介绍","level":"1.11.6","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis锁","level":"1.11.7","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},{"title":"进程内缓存组件 collection.Cache","level":"1.11.12","depth":2,"path":"collection.md","ref":"collection.md","articles":[]},{"title":"高效的关键词替换和敏感词过滤工具","level":"1.11.13","depth":2,"path":"keywords.md","ref":"keywords.md","articles":[]},{"title":"服务自适应降载保护设计","level":"1.11.14","depth":2,"path":"loadshedding.md","ref":"loadshedding.md","articles":[]},{"title":"文本序列化和反序列化","level":"1.11.15","depth":2,"path":"mapping.md","ref":"mapping.md","articles":[]},{"title":"并发处理工具 MapReduce","level":"1.11.16","depth":2,"path":"mapreduce.md","ref":"mapreduce.md","articles":[]},{"title":"基于prometheus的微服务指标监控","level":"1.11.17","depth":2,"path":"metric.md","ref":"metric.md","articles":[]},{"title":"防止缓存击穿之进程内共享调用","level":"1.11.18","depth":2,"path":"sharedcalls.md","ref":"sharedcalls.md","articles":[]},{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"path":"zrpc.md","ref":"zrpc.md","articles":[]},{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"go-zero缓存设计之业务层缓存","level":"1.11.22","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"go-zero分布式定时任务","level":"1.11.23","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"使用go-zero开发一个旅游系统go-zero-looklook","level":"1.11.24","depth":2,"path":"go-zero-looklook.md","ref":"go-zero-looklook.md","articles":[]},{"title":"我是如何用go-zero 实现一个中台系统","level":"1.11.25","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"流数据处理利器","level":"1.11.26","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"10月3日线上交流问题汇总","level":"1.11.27","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"previous":{"title":"模板操作","level":"1.10.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/timing-wheel.html b/cn/timing-wheel.html index fe29758a5..1a13ab133 100644 --- a/cn/timing-wheel.html +++ b/cn/timing-wheel.html @@ -1744,7 +1744,7 @@

    总结

    而时间分隔上,时间轮有 circle 分层,这样就可以不断复用原有的 numSlots ,因为定时器在不断 loop,而执行可以把上层的 slot 下降到下层,在不断 loop 中就可以执行到上层的task。

    go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1789,7 +1789,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"时间轮介绍","level":"1.11.10","depth":2,"next":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},"previous":{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"timing-wheel.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"时间轮介绍","level":"1.11.10","depth":2,"next":{"title":"熔断原理与实现","level":"1.11.11","depth":2,"path":"breaker-algorithms.md","ref":"breaker-algorithms.md","articles":[]},"previous":{"title":"令牌桶限流","level":"1.11.9","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"timing-wheel.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/tips.html b/cn/tips.html index 9003d416e..4997496d4 100644 --- a/cn/tips.html +++ b/cn/tips.html @@ -1517,7 +1517,7 @@

    阅读须知

  • 在遇到问题或错误时,请一定记住多查FAQ
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1562,7 +1562,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"阅读须知","level":"1.2","depth":1,"next":{"title":"关于我们","level":"1.3","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"previous":{"title":"简介","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tips.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"阅读须知","level":"1.2","depth":1,"next":{"title":"关于我们","level":"1.3","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"previous":{"title":"简介","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tips.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/tokenlimit.html b/cn/tokenlimit.html index b0c0d7aa3..bf874eac2 100644 --- a/cn/tokenlimit.html +++ b/cn/tokenlimit.html @@ -1656,7 +1656,7 @@

    参考

  • Go-Redis 提供的分布式限流库
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1701,7 +1701,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"令牌桶限流","level":"1.11.9","depth":2,"next":{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"previous":{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tokenlimit.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"令牌桶限流","level":"1.11.9","depth":2,"next":{"title":"时间轮介绍","level":"1.11.10","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"previous":{"title":"periodlimit限流","level":"1.11.8","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tokenlimit.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/tool-center.html b/cn/tool-center.html index c0fb0aeef..cf02941e4 100644 --- a/cn/tool-center.html +++ b/cn/tool-center.html @@ -1517,7 +1517,7 @@

    工具中心

  • vscode插件
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1562,7 +1562,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"工具中心","level":"1.12.1","depth":2,"next":{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"previous":{"title":"go-zero 生态","level":"1.12","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tool-center.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"工具中心","level":"1.12.1","depth":2,"next":{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"previous":{"title":"go-zero 生态","level":"1.12","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"工具中心","level":"1.12.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"vscode插件","level":"1.12.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"插件中心","level":"1.12.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tool-center.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/trace.html b/cn/trace.html index fd6aafb0c..13ef60feb 100644 --- a/cn/trace.html +++ b/cn/trace.html @@ -1666,7 +1666,7 @@

    参考

  • go-zero trace
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1711,7 +1711,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"链路追踪","level":"1.8.9","depth":2,"next":{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"previous":{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"trace.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"链路追踪","level":"1.8.9","depth":2,"next":{"title":"服务监控","level":"1.8.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"previous":{"title":"日志收集","level":"1.8.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"trace.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/vscode.html b/cn/vscode.html index 5cbedc41b..478d19ddc 100644 --- a/cn/vscode.html +++ b/cn/vscode.html @@ -1541,7 +1541,7 @@

    service 代码块

    handler 代码块

    type

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1586,7 +1586,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"vscode插件","level":"1.12.1.2","depth":3,"next":{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"previous":{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"vscode.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"vscode插件","level":"1.12.1.2","depth":3,"next":{"title":"分布式事务支持","level":"1.12.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"previous":{"title":"intellij插件","level":"1.12.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"vscode.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/wechat.html b/cn/wechat.html index b40274be7..827a15ff7 100644 --- a/cn/wechat.html +++ b/cn/wechat.html @@ -1553,7 +1553,7 @@

    干货

  • 《服务自适应降载保护设计》
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1598,7 +1598,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"公众号","level":"1.13.1","depth":2,"next":{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"previous":{"title":"学习资源","level":"1.13","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"wechat.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"公众号","level":"1.13.1","depth":2,"next":{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"previous":{"title":"学习资源","level":"1.13","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"公众号","level":"1.13.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Go夜读","level":"1.13.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"Go开源说","level":"1.13.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"wechat.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/cn/zrpc.html b/cn/zrpc.html index 225c951c2..01c8586bc 100644 --- a/cn/zrpc.html +++ b/cn/zrpc.html @@ -1967,7 +1967,7 @@

    总结

    接着,介绍了zRPC的几个重要组成模块以及其实现原理,并分析了部分源码。拦截器模块是整个zRPC的重点,其中内置了丰富的功能,像熔断、监控、降载等等也是构建高可用微服务必不可少的。resolver和balancer模块自定义了gRPC的resolver和balancer,通过该部分可以了解到整个服务注册与发现的原理以及如何构建自己的服务发现系统,同时自定义负载均衡算法也变得不再神秘

    最后,zRPC是一个经历过各种工程实践的RPC框架,不论是想要用于生产还是学习其中的设计模式都是一个不可多得的开源项目。希望通过这篇文章的介绍大家能够进一步了解zRPC

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -2012,7 +2012,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"next":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"previous":{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"zrpc.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"cn"}}); + gitbook.page.hasChanged({"page":{"title":"zrpc 使用介绍","level":"1.11.20","depth":2,"next":{"title":"go-zero缓存设计之持久层缓存","level":"1.11.21","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"previous":{"title":"DB缓存机制","level":"1.11.19","depth":2,"path":"sql-cache.md","ref":"sql-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"cn","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"zrpc.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"cn"}}); }); diff --git a/en/about-us.html b/en/about-us.html index 68c02890a..b237cb8bb 100644 --- a/en/about-us.html +++ b/en/about-us.html @@ -1358,7 +1358,7 @@

    Go-Zero Members

    Go-Zero Community

    go-zero has more than 3,000 community members. Here, you can discuss any go-zero technology, feedback on issues, get the latest go-zero information, and the technical experience shared by the big guys every day.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1403,7 +1403,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"About Us","level":"1.2","depth":1,"next":{"title":"Join Us","level":"1.3","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"previous":{"title":"Introduction","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"about-us.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"About Us","level":"1.2","depth":1,"next":{"title":"Join Us","level":"1.3","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"previous":{"title":"Introduction","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"about-us.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/api-coding.html b/en/api-coding.html index 71ddf11cd..6c916bb49 100644 --- a/en/api-coding.html +++ b/en/api-coding.html @@ -1395,7 +1395,7 @@

    Guess you wants

  • API Directory Structure
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1440,7 +1440,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"API Coding","level":"1.7.5.3","depth":3,"next":{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"previous":{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-coding.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"API Coding","level":"1.7.5.3","depth":3,"next":{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"previous":{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-coding.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/api-config.html b/en/api-config.html index a5f18df4a..8fb715085 100644 --- a/en/api-config.html +++ b/en/api-config.html @@ -1439,7 +1439,7 @@

    redis.RedisConf

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1484,7 +1484,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"API Configuration","level":"1.7.4.1","depth":3,"next":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"previous":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-config.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"API Configuration","level":"1.7.4.1","depth":3,"next":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"previous":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-config.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/api-dir.html b/en/api-dir.html index d0cc7dcc7..3912e30ac 100644 --- a/en/api-dir.html +++ b/en/api-dir.html @@ -1373,7 +1373,7 @@

    API directory introduction

    └── types.go
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1418,7 +1418,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"API Directory Structure","level":"1.6.4","depth":2,"next":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"previous":{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-dir.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"API Directory Structure","level":"1.6.4","depth":2,"next":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"previous":{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-dir.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/api-grammar.html b/en/api-grammar.html index c7e8a0922..bac349594 100644 --- a/en/api-grammar.html +++ b/en/api-grammar.html @@ -1949,7 +1949,7 @@

    Doc&Comment

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1994,7 +1994,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"API IDL","level":"1.6.3","depth":2,"next":{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"previous":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-grammar.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"API IDL","level":"1.6.3","depth":2,"next":{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"previous":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"api-grammar.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/bloom.html b/en/bloom.html index d1d70a144..188b13e51 100644 --- a/en/bloom.html +++ b/en/bloom.html @@ -1423,7 +1423,7 @@

    Bloom filter Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50 @@ -1468,7 +1468,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"bloom","level":"1.10.2","depth":2,"next":{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"previous":{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"bloom.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"bloom","level":"1.10.2","depth":2,"next":{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"previous":{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"bloom.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/buiness-cache.html b/en/buiness-cache.html index a39539120..e0d996af6 100644 --- a/en/buiness-cache.html +++ b/en/buiness-cache.html @@ -1509,7 +1509,7 @@

    Summary

    there is no need to put biz redis directly in it. You can try to let db take care of it first, and developers can monitor according to the persistence layer. And service Biz needs to be introduced when monitoring to measure.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1554,7 +1554,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Business layer cache","level":"1.13.2","depth":2,"next":{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"previous":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"buiness-cache.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Business layer cache","level":"1.13.2","depth":2,"next":{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"previous":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"buiness-cache.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/business-coding.html b/en/business-coding.html index 48b253551..a4bee7946 100644 --- a/en/business-coding.html +++ b/en/business-coding.html @@ -1461,7 +1461,7 @@

    Guess you wants

  • API Configuration
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1506,7 +1506,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Business Coding","level":"1.7.5.4","depth":3,"next":{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"previous":{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-coding.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Business Coding","level":"1.7.5.4","depth":3,"next":{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"previous":{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-coding.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/business-dev.html b/en/business-dev.html index 670b9d154..91d87436f 100644 --- a/en/business-dev.html +++ b/en/business-dev.html @@ -1411,7 +1411,7 @@

    Reference preset data

    INSERT INTO `user` (number,name,password,gender)values ('666','xiaoming','123456','male');
     
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1456,7 +1456,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Business Development","level":"1.7.5","depth":2,"next":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"previous":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-dev.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Business Development","level":"1.7.5","depth":2,"next":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"previous":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"business-dev.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/ci-cd.html b/en/ci-cd.html index bb8ae07d4..6ff9b00c5 100644 --- a/en/ci-cd.html +++ b/en/ci-cd.html @@ -1407,7 +1407,7 @@

    Reference

  • Gitlab CI
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1452,7 +1452,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"CI/CD","level":"1.7.6","depth":2,"next":{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"previous":{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"ci-cd.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"CI/CD","level":"1.7.6","depth":2,"next":{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"previous":{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"ci-cd.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/coding-spec.html b/en/coding-spec.html index 1ee624689..91f1e244c 100644 --- a/en/coding-spec.html +++ b/en/coding-spec.html @@ -1398,7 +1398,7 @@

    Function body coding

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1443,7 +1443,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"next":{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"previous":{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"coding-spec.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"next":{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"previous":{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"coding-spec.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/concept-introduction.html b/en/concept-introduction.html index abe18d38f..ccf5d001c 100644 --- a/en/concept-introduction.html +++ b/en/concept-introduction.html @@ -1382,7 +1382,7 @@

    Reference

  • API IDL
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1427,7 +1427,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Concepts","level":"1.4","depth":1,"next":{"title":"Quick Start","level":"1.5","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"previous":{"title":"Join Us","level":"1.3","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"concept-introduction.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Concepts","level":"1.4","depth":1,"next":{"title":"Quick Start","level":"1.5","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"previous":{"title":"Join Us","level":"1.3","depth":1,"path":"join-us.md","ref":"join-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"concept-introduction.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/config-introduction.html b/en/config-introduction.html index cc83af2bd..8216b3035 100644 --- a/en/config-introduction.html +++ b/en/config-introduction.html @@ -1357,7 +1357,7 @@

    Configuration Introduction

  • RPC Configuration
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1402,7 +1402,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"next":{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"previous":{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"config-introduction.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"next":{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"previous":{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"config-introduction.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/contributor.html b/en/contributor.html index 0ca370dd3..fe3997026 100644 --- a/en/contributor.html +++ b/en/contributor.html @@ -1458,6 +1458,13 @@

    go-zero contributors

    + + @@ -1514,13 +1521,6 @@

    go-zero contributors

    - - - - - - + + + + @@ -1802,16 +1802,16 @@

    go-zero contributors

    @@ -1837,100 +1837,100 @@

    go-zero contributors

    @@ -1942,23 +1942,30 @@

    go-zero contributors

    + + @@ -2026,16 +2033,16 @@

    go-zero contributors

    @@ -2173,9 +2180,16 @@

    go-zero contributors

    + + @@ -2193,6 +2207,13 @@

    go-zero contributors

    + + + + - - - - - - - - + + + + - - @@ -2844,6 +2851,13 @@

    go-zero contributors

    + + - -
    codeErrorSleep @@ -3210,7 +3217,7 @@

    Document contributors

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:18 +2024-03-02 02:15:51
    @@ -3255,7 +3262,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Contributor","level":"1.14","depth":1,"next":{"title":"Document Contribute","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"previous":{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"contributor.md","mtime":"2024-03-01T16:09:18.949Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Contributor","level":"1.14","depth":1,"next":{"title":"Document Contribute","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"previous":{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"contributor.md","mtime":"2024-03-02T02:15:51.882Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); });

    diff --git a/en/datacenter.html b/en/datacenter.html index 029584e8d..2c64cac12 100644 --- a/en/datacenter.html +++ b/en/datacenter.html @@ -2109,7 +2109,7 @@

    Reviews

    root:password@/name?parseTime=True&loc=Local&charset=utf8 is modified to: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -2154,7 +2154,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Middle Ground System","level":"1.13.4","depth":2,"next":{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"previous":{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"datacenter.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Middle Ground System","level":"1.13.4","depth":2,"next":{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"previous":{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"datacenter.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/dev-flow.html b/en/dev-flow.html index bb53c8cf5..ee960d677 100644 --- a/en/dev-flow.html +++ b/en/dev-flow.html @@ -1379,7 +1379,7 @@

    Development Tools

  • Goland (recommended)
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1424,7 +1424,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Development Flow","level":"1.7.3","depth":2,"next":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"previous":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-flow.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Development Flow","level":"1.7.3","depth":2,"next":{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},"previous":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-flow.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/dev-specification.html b/en/dev-specification.html index 8f778ef6f..5b779dfbe 100644 --- a/en/dev-specification.html +++ b/en/dev-specification.html @@ -1373,7 +1373,7 @@

    Productivity)

    In the go-zero team, this topic has always been emphasized. The productivity of developers is not how many lines of code you have written and how many module developments you have completed, but we need to use various effective ways to take advantage of the limited Time to complete the development to maximize the efficiency, and the birth of Goctl was officially to increase productivity, Therefore, I very much agree with this development principle.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1418,7 +1418,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Development Rules","level":"1.7.2","depth":2,"next":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"previous":{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-specification.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Development Rules","level":"1.7.2","depth":2,"next":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"previous":{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"dev-specification.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/distributed-transaction.html b/en/distributed-transaction.html index e92567cce..52a5a117f 100644 --- a/en/distributed-transaction.html +++ b/en/distributed-transaction.html @@ -1372,7 +1372,7 @@

    More application scenarios

    For more information on dtm's capabilities and presentation, see dtm

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1417,7 +1417,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"next":{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"previous":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"distributed-transaction.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"next":{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"previous":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"distributed-transaction.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/doc-contibute.html b/en/doc-contibute.html index e4588430e..4973d9e59 100644 --- a/en/doc-contibute.html +++ b/en/doc-contibute.html @@ -1405,7 +1405,7 @@

    Guess you wants

  • Github Pull request
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1450,7 +1450,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Document Contribute","level":"1.15","depth":1,"next":{"title":"Error","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"previous":{"title":"Contributor","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"doc-contibute.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Document Contribute","level":"1.15","depth":1,"next":{"title":"Error","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"previous":{"title":"Contributor","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"doc-contibute.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/eco.html b/en/eco.html index bd71415d3..fd1ef4d61 100644 --- a/en/eco.html +++ b/en/eco.html @@ -1357,7 +1357,7 @@

    go-zero around

  • Plugins
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1402,7 +1402,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Around","level":"1.11","depth":1,"next":{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"previous":{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"eco.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Around","level":"1.11","depth":1,"next":{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"previous":{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"eco.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/error-handle.html b/en/error-handle.html index 2db753ada..e91ed4e61 100644 --- a/en/error-handle.html +++ b/en/error-handle.html @@ -1518,7 +1518,7 @@

    Custom error

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1563,7 +1563,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Error Handling","level":"1.7.5.8","depth":3,"next":{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"previous":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error-handle.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Error Handling","level":"1.7.5.8","depth":3,"next":{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"previous":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error-handle.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/error.html b/en/error.html index 870df65c9..9835cedb6 100644 --- a/en/error.html +++ b/en/error.html @@ -1384,7 +1384,7 @@

    command not found: goctl

    command not found: goctl
     

    Please make sure that goctl has been installed or whether goctl has been added to the environment variable

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1429,7 +1429,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Error","level":"1.16","depth":1,"next":{"title":"Source Code","level":"1.17","depth":1,"path":"source.md","ref":"source.md","articles":[]},"previous":{"title":"Document Contribute","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Error","level":"1.16","depth":1,"next":{"title":"Source Code","level":"1.17","depth":1,"path":"source.md","ref":"source.md","articles":[]},"previous":{"title":"Document Contribute","level":"1.15","depth":1,"path":"doc-contibute.md","ref":"doc-contibute.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"error.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/executors.html b/en/executors.html index 867d627ab..103c0dbfc 100644 --- a/en/executors.html +++ b/en/executors.html @@ -1642,7 +1642,7 @@

    Summary

    There are many useful component tools in go-zero. Good use of tools is very helpful to improve service performance and development efficiency. I hope this article can bring you some gains.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1687,7 +1687,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"executors","level":"1.10.3","depth":2,"next":{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"previous":{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"executors.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"executors","level":"1.10.3","depth":2,"next":{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"previous":{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"executors.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/extended-reading.html b/en/extended-reading.html index a6b9bc104..50f0d284e 100644 --- a/en/extended-reading.html +++ b/en/extended-reading.html @@ -1366,7 +1366,7 @@

    Components

  • TimingWheel
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1411,7 +1411,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Extended","level":"1.10","depth":1,"next":{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"previous":{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"extended-reading.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Extended","level":"1.10","depth":1,"next":{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},"previous":{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"extended-reading.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/framework-design.html b/en/framework-design.html index f0f994bc4..48763dea0 100644 --- a/en/framework-design.html +++ b/en/framework-design.html @@ -1361,7 +1361,7 @@

    Framework Design

  • RPC Directory Structure
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1406,7 +1406,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Framework Design","level":"1.6","depth":1,"next":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"previous":{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"framework-design.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Framework Design","level":"1.6","depth":1,"next":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"previous":{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"framework-design.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/fx.html b/en/fx.html index 5d073c316..dbf0a35d8 100644 --- a/en/fx.html +++ b/en/fx.html @@ -1601,7 +1601,7 @@

    Reference

  • Stream API in Java 8
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1646,7 +1646,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"fx","level":"1.10.4","depth":2,"next":{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"previous":{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"fx.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"fx","level":"1.10.4","depth":2,"next":{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"previous":{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"fx.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/go-queue.html b/en/go-queue.html index 68ad38583..16d4bd3d6 100644 --- a/en/go-queue.html +++ b/en/go-queue.html @@ -1617,7 +1617,7 @@

    Queue

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1662,7 +1662,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Queue","level":"1.13.3","depth":2,"next":{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"previous":{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-queue.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Queue","level":"1.13.3","depth":2,"next":{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"previous":{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-queue.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/go-zero-design.html b/en/go-zero-design.html index 2461e13db..95764a7da 100644 --- a/en/go-zero-design.html +++ b/en/go-zero-design.html @@ -1363,7 +1363,7 @@

    Go-Zero Design

  • There is only one way to constrain one thing
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1408,7 +1408,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"next":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"previous":{"title":"Framework Design","level":"1.6","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-design.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"next":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},"previous":{"title":"Framework Design","level":"1.6","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-design.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/go-zero-features.html b/en/go-zero-features.html index 5f9589a10..552d0ed64 100644 --- a/en/go-zero-features.html +++ b/en/go-zero-features.html @@ -1370,7 +1370,7 @@

    Go-Zero Features

    As shown in the figure below, we have ensured the high availability of the overall service from multiple levels:

    resilience

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1415,7 +1415,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"next":{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"previous":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-features.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Go-Zero Features","level":"1.6.2","depth":2,"next":{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},"previous":{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"go-zero-features.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-api.html b/en/goctl-api.html index b244f828b..fff8017e8 100644 --- a/en/goctl-api.html +++ b/en/goctl-api.html @@ -1414,7 +1414,7 @@

    Guess you wants

  • API Directory Structure
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1459,7 +1459,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"API Commands","level":"1.8.3","depth":2,"next":{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"previous":{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-api.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"API Commands","level":"1.8.3","depth":2,"next":{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"previous":{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-api.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-commands.html b/en/goctl-commands.html index 91dfaa743..6d3389cfe 100644 --- a/en/goctl-commands.html +++ b/en/goctl-commands.html @@ -1633,7 +1633,7 @@

    completion

    (generation completion script, it only works for unix-like OS)

    --name, -n: the filename of auto complete script, default is [goctl_autocomplete]

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1678,7 +1678,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Commands & Flags","level":"1.8.2","depth":2,"next":{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"previous":{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-commands.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Commands & Flags","level":"1.8.2","depth":2,"next":{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"previous":{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-commands.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-completion.html b/en/goctl-completion.html index 5f894cc8e..863320c08 100644 --- a/en/goctl-completion.html +++ b/en/goctl-completion.html @@ -1393,7 +1393,7 @@

    Demo effect

    Translated with www.DeepL.com/Translator (free version)

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1438,7 +1438,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Auto Completion","level":"1.8.1","depth":2,"next":{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"previous":{"title":"Goctl","level":"1.8","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-completion.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Auto Completion","level":"1.8.1","depth":2,"next":{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},"previous":{"title":"Goctl","level":"1.8","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-completion.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-install.html b/en/goctl-install.html index f40570e19..32779a498 100644 --- a/en/goctl-install.html +++ b/en/goctl-install.html @@ -1388,7 +1388,7 @@

    Install(mac&linux)

    For windows users to add environment variables, please Google by yourself.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1433,7 +1433,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"next":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"previous":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-install.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"next":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"previous":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-install.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-model.html b/en/goctl-model.html index 744a29a0d..bf418c572 100644 --- a/en/goctl-model.html +++ b/en/goctl-model.html @@ -1772,7 +1772,7 @@

    Type conversion rules

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1817,7 +1817,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Model Commands","level":"1.8.5","depth":2,"next":{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"previous":{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-model.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Model Commands","level":"1.8.5","depth":2,"next":{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"previous":{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-model.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-other.html b/en/goctl-other.html index a21ff573e..49b5ddf26 100644 --- a/en/goctl-other.html +++ b/en/goctl-other.html @@ -1637,7 +1637,7 @@

    Guess you wants

  • K8s
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1682,7 +1682,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"More Commands","level":"1.8.7","depth":2,"next":{"title":"Template","level":"1.9","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"previous":{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-other.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"More Commands","level":"1.8.7","depth":2,"next":{"title":"Template","level":"1.9","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"previous":{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-other.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-plugin.html b/en/goctl-plugin.html index a0e1fdd12..22aae83c4 100644 --- a/en/goctl-plugin.html +++ b/en/goctl-plugin.html @@ -1406,7 +1406,7 @@

    Guess you wants

  • API Commands
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1451,7 +1451,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Plugin Commands","level":"1.8.6","depth":2,"next":{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"previous":{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-plugin.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Plugin Commands","level":"1.8.6","depth":2,"next":{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"previous":{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-plugin.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl-rpc.html b/en/goctl-rpc.html index e8b5ff83f..dbfd82057 100644 --- a/en/goctl-rpc.html +++ b/en/goctl-rpc.html @@ -1540,7 +1540,7 @@

    Guess what you want to see

  • rpc-call
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1585,7 +1585,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"RPC Commands","level":"1.8.4","depth":2,"next":{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"previous":{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-rpc.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"RPC Commands","level":"1.8.4","depth":2,"next":{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},"previous":{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl-rpc.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goctl.html b/en/goctl.html index ebfd65bf0..2759b8639 100644 --- a/en/goctl.html +++ b/en/goctl.html @@ -1401,7 +1401,7 @@

    FAQ

    command not found: goctl
     

    Please make sure that goctl has been installed, or whether goctl has been correctly added to the environment variables of the current shell.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1446,7 +1446,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Goctl","level":"1.8","depth":1,"next":{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"previous":{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Goctl","level":"1.8","depth":1,"next":{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},"previous":{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goctl.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/golang-install.html b/en/golang-install.html index 17d198687..a664692b3 100644 --- a/en/golang-install.html +++ b/en/golang-install.html @@ -1391,7 +1391,7 @@

    Install Go on windows

    More

    For more operating system installation, see https://golang.org/dl/

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1436,7 +1436,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"next":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"previous":{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"golang-install.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"next":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},"previous":{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"golang-install.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/gomod-config.html b/en/gomod-config.html index 55bb7993a..4d69edcca 100644 --- a/en/gomod-config.html +++ b/en/gomod-config.html @@ -1381,7 +1381,7 @@

    Module configuration

    Reference

    [1] Go Modules Reference

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1426,7 +1426,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"next":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"previous":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gomod-config.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"next":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"previous":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gomod-config.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/goreading.html b/en/goreading.html index 3d86f211c..64111287d 100644 --- a/en/goreading.html +++ b/en/goreading.html @@ -1359,7 +1359,7 @@

    Night

  • Goodbye go-micro! Enterprise project migration go-zero strategy (1)
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1404,7 +1404,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Night","level":"1.12.2","depth":2,"next":{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"previous":{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goreading.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Night","level":"1.12.2","depth":2,"next":{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"previous":{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"goreading.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/gotalk.html b/en/gotalk.html index 57e0a0daa..4ab2ccc5d 100644 --- a/en/gotalk.html +++ b/en/gotalk.html @@ -1355,7 +1355,7 @@

    OpenTalk

  • OpenTalk 4th - Go-Zero
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1400,7 +1400,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"OpenTalk","level":"1.12.3","depth":2,"next":{"title":"User Practise","level":"1.13","depth":1,"path":"practise.md","ref":"practise.md","articles":[{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"previous":{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gotalk.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"OpenTalk","level":"1.12.3","depth":2,"next":{"title":"User Practise","level":"1.13","depth":1,"path":"practise.md","ref":"practise.md","articles":[{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"previous":{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"gotalk.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/index.html b/en/index.html index 8b6cbe036..4f5c53994 100644 --- a/en/index.html +++ b/en/index.html @@ -1523,7 +1523,7 @@

    9. Important notes

    10. Chat group

    Join the chat via https://join.slack.com/t/go-zeroworkspace/shared_invite/zt-m39xssxc-kgIqERa7aVsujKNj~XuPKg

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1564,7 +1564,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Introduction","level":"1.1","depth":1,"next":{"title":"About Us","level":"1.2","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"README.md","mtime":"2024-03-01T16:09:17.401Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Introduction","level":"1.1","depth":1,"next":{"title":"About Us","level":"1.2","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"README.md","mtime":"2024-03-02T02:15:50.582Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/intellij.html b/en/intellij.html index 1b72e7291..6c3977efa 100644 --- a/en/intellij.html +++ b/en/intellij.html @@ -1523,7 +1523,7 @@

    Default template and effective sco

    context menu

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1568,7 +1568,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"next":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"previous":{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"intellij.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"next":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]},"previous":{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"intellij.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/join-us.html b/en/join-us.html index 0868c4959..d697db602 100644 --- a/en/join-us.html +++ b/en/join-us.html @@ -1406,7 +1406,7 @@

    Reference

  • Github Pull request
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1451,7 +1451,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Join Us","level":"1.3","depth":1,"next":{"title":"Concepts","level":"1.4","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"previous":{"title":"About Us","level":"1.2","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"join-us.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Join Us","level":"1.3","depth":1,"next":{"title":"Concepts","level":"1.4","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"previous":{"title":"About Us","level":"1.2","depth":1,"path":"about-us.md","ref":"about-us.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"join-us.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/jwt.html b/en/jwt.html index a79244aac..a0138df56 100644 --- a/en/jwt.html +++ b/en/jwt.html @@ -1552,7 +1552,7 @@

    Guess you wants

  • API IDL
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1597,7 +1597,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"JWT","level":"1.7.5.5","depth":3,"next":{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"previous":{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"jwt.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"JWT","level":"1.7.5.5","depth":3,"next":{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"previous":{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"jwt.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/learning-resource.html b/en/learning-resource.html index 44b5f4b4d..011bb25da 100644 --- a/en/learning-resource.html +++ b/en/learning-resource.html @@ -1358,7 +1358,7 @@

    Learning Resources

  • OpenTalk
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1403,7 +1403,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Learning Resources","level":"1.12","depth":1,"next":{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"previous":{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"learning-resource.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Learning Resources","level":"1.12","depth":1,"next":{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},"previous":{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"learning-resource.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/log-collection.html b/en/log-collection.html index 8c38e8048..6804a0c44 100644 --- a/en/log-collection.html +++ b/en/log-collection.html @@ -1487,7 +1487,7 @@

    Reference

  • filebeat
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1532,7 +1532,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Log Collection","level":"1.7.8","depth":2,"next":{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"previous":{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"log-collection.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Log Collection","level":"1.7.8","depth":2,"next":{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"previous":{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"log-collection.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/logx.html b/en/logx.html index ffde8f6f8..615fef63d 100644 --- a/en/logx.html +++ b/en/logx.html @@ -1491,7 +1491,7 @@

    KeepDays

    StackCooldownMillis

    StackCooldownMillis defines the log output interval, the default is 100 milliseconds.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1536,7 +1536,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"logx","level":"1.10.1","depth":2,"next":{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"previous":{"title":"Extended","level":"1.10","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"logx.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"logx","level":"1.10.1","depth":2,"next":{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},"previous":{"title":"Extended","level":"1.10","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"logx.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/micro-service.html b/en/micro-service.html index 2ff975432..03c471ac9 100644 --- a/en/micro-service.html +++ b/en/micro-service.html @@ -1658,7 +1658,7 @@

    Guess what you want to see

  • rpc caller description
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1703,7 +1703,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Micro Service","level":"1.5.2","depth":2,"next":{"title":"Framework Design","level":"1.6","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"previous":{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"micro-service.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Micro Service","level":"1.5.2","depth":2,"next":{"title":"Framework Design","level":"1.6","depth":1,"path":"framework-design.md","ref":"framework-design.md","articles":[{"title":"Go-Zero Design","level":"1.6.1","depth":2,"path":"go-zero-design.md","ref":"go-zero-design.md","articles":[]},{"title":"Go-Zero Features","level":"1.6.2","depth":2,"path":"go-zero-features.md","ref":"go-zero-features.md","articles":[]},{"title":"API IDL","level":"1.6.3","depth":2,"path":"api-grammar.md","ref":"api-grammar.md","articles":[]},{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]}]},"previous":{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"micro-service.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/middleware.html b/en/middleware.html index cdaa67c9e..32261c27f 100644 --- a/en/middleware.html +++ b/en/middleware.html @@ -1491,7 +1491,7 @@

    Call another service within

    For full example, see: https://github.com/zeromicro/zero-examples/tree/main/http/middleware

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1536,7 +1536,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Middleware","level":"1.7.5.6","depth":3,"next":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"previous":{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"middleware.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Middleware","level":"1.7.5.6","depth":3,"next":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},"previous":{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"middleware.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/model-gen.html b/en/model-gen.html index dfd786b64..75ce56c46 100644 --- a/en/model-gen.html +++ b/en/model-gen.html @@ -1399,7 +1399,7 @@

    More

    Guess you wants

    Model Commands

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1444,7 +1444,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Model Generation","level":"1.7.5.2","depth":3,"next":{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"previous":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"model-gen.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Model Generation","level":"1.7.5.2","depth":3,"next":{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},"previous":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"model-gen.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/monolithic-service.html b/en/monolithic-service.html index 2ba262a52..988075bd0 100644 --- a/en/monolithic-service.html +++ b/en/monolithic-service.html @@ -1425,7 +1425,7 @@

    Guess you wants

  • Middleware
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1470,7 +1470,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Monolithic Service","level":"1.5.1","depth":2,"next":{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"previous":{"title":"Quick Start","level":"1.5","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"monolithic-service.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Monolithic Service","level":"1.5.1","depth":2,"next":{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]},"previous":{"title":"Quick Start","level":"1.5","depth":1,"path":"quick-start.md","ref":"quick-start.md","articles":[{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},{"title":"Micro Service","level":"1.5.2","depth":2,"path":"micro-service.md","ref":"micro-service.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"monolithic-service.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/mysql.html b/en/mysql.html index 7036faee8..dadc1a2c7 100644 --- a/en/mysql.html +++ b/en/mysql.html @@ -1502,7 +1502,7 @@

    Transaction

    Distributed transactions

    go-zero has deeply cooperated with dtm and has natively supported distributed transactions, see distributed-transaction for details

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1547,7 +1547,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"mysql","level":"1.10.5","depth":2,"next":{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"previous":{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mysql.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"mysql","level":"1.10.5","depth":2,"next":{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"previous":{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"mysql.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/naming-spec.html b/en/naming-spec.html index 42f268b74..fffd318e5 100644 --- a/en/naming-spec.html +++ b/en/naming-spec.html @@ -1406,7 +1406,7 @@

    Reference

  • Practical Go: Real world advice for writing maintainable Go programs
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1451,7 +1451,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"next":{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"previous":{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"naming-spec.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"next":{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},"previous":{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"naming-spec.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/online-exchange.html b/en/online-exchange.html index 967068925..f8a7a3d98 100644 --- a/en/online-exchange.html +++ b/en/online-exchange.html @@ -1552,7 +1552,7 @@

    Summary of onli
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1597,7 +1597,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Online Exchange","level":"1.13.6","depth":2,"next":{"title":"Contributor","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"previous":{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"online-exchange.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Online Exchange","level":"1.13.6","depth":2,"next":{"title":"Contributor","level":"1.14","depth":1,"path":"contributor.md","ref":"contributor.md","articles":[]},"previous":{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"online-exchange.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/periodlimit.html b/en/periodlimit.html index 5b97c7d51..2b0985a71 100644 --- a/en/periodlimit.html +++ b/en/periodlimit.html @@ -1488,7 +1488,7 @@

    Reference

  • tokenlimit
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1533,7 +1533,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"periodlimit","level":"1.10.7","depth":2,"next":{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"previous":{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"periodlimit.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"periodlimit","level":"1.10.7","depth":2,"next":{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"previous":{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"periodlimit.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/plugin-center.html b/en/plugin-center.html index 39dd4b859..f8b48676d 100644 --- a/en/plugin-center.html +++ b/en/plugin-center.html @@ -1369,7 +1369,7 @@

    Guess you wants

  • API IDL
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1414,7 +1414,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Plugins","level":"1.11.3","depth":2,"next":{"title":"Learning Resources","level":"1.12","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"previous":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"plugin-center.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Plugins","level":"1.11.3","depth":2,"next":{"title":"Learning Resources","level":"1.12","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"previous":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"plugin-center.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/practise.html b/en/practise.html index d4ece3deb..e0e5b6516 100644 --- a/en/practise.html +++ b/en/practise.html @@ -1360,7 +1360,7 @@

    User Practise

  • Online Exchange
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1405,7 +1405,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"User Practise","level":"1.13","depth":1,"next":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"previous":{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"practise.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"User Practise","level":"1.13","depth":1,"next":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},"previous":{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"practise.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/prepare-other.html b/en/prepare-other.html index 9cbf1ee0f..70f352396 100644 --- a/en/prepare-other.html +++ b/en/prepare-other.html @@ -1361,7 +1361,7 @@

    Other environment

  • mysql
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1406,7 +1406,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"More","level":"1.7.1.5","depth":3,"next":{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"previous":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare-other.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"More","level":"1.7.1.5","depth":3,"next":{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},"previous":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare-other.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/prepare.html b/en/prepare.html index da22581c4..6c20d7efd 100644 --- a/en/prepare.html +++ b/en/prepare.html @@ -1361,7 +1361,7 @@

    Prepare

  • More
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1406,7 +1406,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Prepare","level":"1.7.1","depth":2,"next":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"previous":{"title":"Project Development","level":"1.7","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Prepare","level":"1.7.1","depth":2,"next":{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},"previous":{"title":"Project Development","level":"1.7","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"prepare.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/project-dev.html b/en/project-dev.html index 6df8f1b24..ff0a09c62 100644 --- a/en/project-dev.html +++ b/en/project-dev.html @@ -1391,7 +1391,7 @@

    Project Development

  • Monitor
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1436,7 +1436,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Project Development","level":"1.7","depth":1,"next":{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"previous":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"project-dev.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Project Development","level":"1.7","depth":1,"next":{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},"previous":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"path":"rpc-dir.md","ref":"rpc-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"project-dev.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/protoc-install.html b/en/protoc-install.html index bd36e0229..0755b5000 100644 --- a/en/protoc-install.html +++ b/en/protoc-install.html @@ -1416,7 +1416,7 @@

    protoc-gen-* installation

    protoc-gen-go installation failed, please read Error

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1461,7 +1461,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"next":{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"previous":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"protoc-install.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"next":{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]},"previous":{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"protoc-install.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/quick-start.html b/en/quick-start.html index 08cf6a0f6..177187293 100644 --- a/en/quick-start.html +++ b/en/quick-start.html @@ -1357,7 +1357,7 @@

    Quick Start

  • micro service
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1402,7 +1402,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Quick Start","level":"1.5","depth":1,"next":{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"previous":{"title":"Concepts","level":"1.4","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"quick-start.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Quick Start","level":"1.5","depth":1,"next":{"title":"Monolithic Service","level":"1.5.1","depth":2,"path":"monolithic-service.md","ref":"monolithic-service.md","articles":[]},"previous":{"title":"Concepts","level":"1.4","depth":1,"path":"concept-introduction.md","ref":"concept-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"quick-start.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/redis-cache.html b/en/redis-cache.html index 869984c29..01346a9c5 100644 --- a/en/redis-cache.html +++ b/en/redis-cache.html @@ -1597,7 +1597,7 @@

    Guess you wants

  • Goctl
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1642,7 +1642,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"next":{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"previous":{"title":"User Practise","level":"1.13","depth":1,"path":"practise.md","ref":"practise.md","articles":[{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-cache.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Persistent layer cache","level":"1.13.1","depth":2,"next":{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},"previous":{"title":"User Practise","level":"1.13","depth":1,"path":"practise.md","ref":"practise.md","articles":[{"title":"Persistent layer cache","level":"1.13.1","depth":2,"path":"redis-cache.md","ref":"redis-cache.md","articles":[]},{"title":"Business layer cache","level":"1.13.2","depth":2,"path":"buiness-cache.md","ref":"buiness-cache.md","articles":[]},{"title":"Queue","level":"1.13.3","depth":2,"path":"go-queue.md","ref":"go-queue.md","articles":[]},{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},{"title":"Stream Handler","level":"1.13.5","depth":2,"path":"stream.md","ref":"stream.md","articles":[]},{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-cache.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/redis-lock.html b/en/redis-lock.html index e8e7ffd7b..ca21620c6 100644 --- a/en/redis-lock.html +++ b/en/redis-lock.html @@ -1470,7 +1470,7 @@

    Release lock

    Can't release other people's locks, can't release other people's locks, can't release other people's locks

    Therefore, you need to first get(key) == value「key」, and then go to delete if it is true

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1515,7 +1515,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"redis-lock","level":"1.10.6","depth":2,"next":{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"previous":{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-lock.md","mtime":"2024-03-01T16:09:17.405Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"redis-lock","level":"1.10.6","depth":2,"next":{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"previous":{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"redis-lock.md","mtime":"2024-03-02T02:15:50.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/route-naming-spec.html b/en/route-naming-spec.html index a113d13a6..852fc9ad2 100644 --- a/en/route-naming-spec.html +++ b/en/route-naming-spec.html @@ -1361,7 +1361,7 @@

    Route Rules

    /user/password/change/:id
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1406,7 +1406,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Route Rules","level":"1.7.2.2","depth":3,"next":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"previous":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"route-naming-spec.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Route Rules","level":"1.7.2.2","depth":3,"next":{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]},"previous":{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"route-naming-spec.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/rpc-call.html b/en/rpc-call.html index 2e0900e62..effb4f6bd 100644 --- a/en/rpc-call.html +++ b/en/rpc-call.html @@ -1584,7 +1584,7 @@

    Guess you wants

  • RPC Commands
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1629,7 +1629,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"next":{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"previous":{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-call.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"next":{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]},"previous":{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-call.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/rpc-config.html b/en/rpc-config.html index 043c40415..aa8d42f9f 100644 --- a/en/rpc-config.html +++ b/en/rpc-config.html @@ -1394,7 +1394,7 @@

    redis.RedisKeyConf

    }
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1439,7 +1439,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"next":{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"previous":{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-config.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"next":{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"previous":{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-config.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/rpc-dir.html b/en/rpc-dir.html index e08cd6f16..1a7a4559c 100644 --- a/en/rpc-dir.html +++ b/en/rpc-dir.html @@ -1413,7 +1413,7 @@

    goctl rpc proto

    [1] The directory where the pb.go & _grpc.pb.go files are located is not fixed, it is determined by go_opt & go-grpc_opt in conjunction with the go_package value in the proto file, to understand the logic of the grpc code generation directory read Go Generated Code

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1458,7 +1458,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"next":{"title":"Project Development","level":"1.7","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"previous":{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-dir.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"RPC Directory Structure","level":"1.6.5","depth":2,"next":{"title":"Project Development","level":"1.7","depth":1,"path":"project-dev.md","ref":"project-dev.md","articles":[{"title":"Prepare","level":"1.7.1","depth":2,"path":"prepare.md","ref":"prepare.md","articles":[{"title":"Golang Installation","level":"1.7.1.1","depth":3,"path":"golang-install.md","ref":"golang-install.md","articles":[]},{"title":"Go Module Configuration","level":"1.7.1.2","depth":3,"path":"gomod-config.md","ref":"gomod-config.md","articles":[]},{"title":"Goctl Installation","level":"1.7.1.3","depth":3,"path":"goctl-install.md","ref":"goctl-install.md","articles":[]},{"title":"protoc & protoc-gen-go Installation","level":"1.7.1.4","depth":3,"path":"protoc-install.md","ref":"protoc-install.md","articles":[]},{"title":"More","level":"1.7.1.5","depth":3,"path":"prepare-other.md","ref":"prepare-other.md","articles":[]}]},{"title":"Development Rules","level":"1.7.2","depth":2,"path":"dev-specification.md","ref":"dev-specification.md","articles":[{"title":"Naming Rules","level":"1.7.2.1","depth":3,"path":"naming-spec.md","ref":"naming-spec.md","articles":[]},{"title":"Route Rules","level":"1.7.2.2","depth":3,"path":"route-naming-spec.md","ref":"route-naming-spec.md","articles":[]},{"title":"Coding Rules","level":"1.7.2.3","depth":3,"path":"coding-spec.md","ref":"coding-spec.md","articles":[]}]},{"title":"Development Flow","level":"1.7.3","depth":2,"path":"dev-flow.md","ref":"dev-flow.md","articles":[]},{"title":"Configuration Introduction","level":"1.7.4","depth":2,"path":"config-introduction.md","ref":"config-introduction.md","articles":[{"title":"API Configuration","level":"1.7.4.1","depth":3,"path":"api-config.md","ref":"api-config.md","articles":[]},{"title":"RPC Configuration","level":"1.7.4.2","depth":3,"path":"rpc-config.md","ref":"rpc-config.md","articles":[]}]},{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},{"title":"Service Deployment","level":"1.7.7","depth":2,"path":"service-deployment.md","ref":"service-deployment.md","articles":[]},{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]}]},"previous":{"title":"API Directory Structure","level":"1.6.4","depth":2,"path":"api-dir.md","ref":"api-dir.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"rpc-dir.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/search_plus_index.json b/en/search_plus_index.json index 5d8f264dc..4a0f06dba 100644 --- a/en/search_plus_index.json +++ b/en/search_plus_index.json @@ -1 +1 @@ -{"./":{"url":"./","title":"Introduction","keywords":"","body":" go-zero 0. what is go-zero go-zero is a web and rpc framework that with lots of engineering practices builtin. It’s born to ensure the stability of the busy services with resilience design, and has been serving sites with tens of millions users for years. go-zero contains simple API description syntax and code generation tool called goctl. You can generate Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript from .api files with goctl. Advantages of go-zero: improve the stability of the services with tens of millions of daily active users builtin chained timeout control, concurrency control, rate limit, adaptive circuit breaker, adaptive load shedding, even no configuration needed builtin middlewares also can be integrated into your frameworks simple API syntax, one command to generate couple of different languages auto validate the request parameters from clients plenty of builtin microservice management and concurrent toolkits 1. Backgrounds of go-zero At the beginning of 2018, we decided to re-design our system, from monolithic architecture with Java+MongoDB to microservice architecture. After researches and comparison, we chose to: Golang based great performance simple syntax proven engineering efficiency extreme deployment experience less server resource consumption Self-designed microservice architecture I have rich experience on designing microservice architectures easy to location the problems easy to extend the features 2. Design considerations on go-zero By designing the microservice architecture, we expected to ensure the stability, as well as the productivity. And from just the beginning, we have the following design principles: keep it simple high availability stable on high concurrency easy to extend resilience design, failure-oriented programming try best to be friendly to the business logic development, encapsulate the complexity one thing, one way After almost half a year, we finished the transfer from monolithic system to microservice system, and deployed on August 2018. The new system guaranteed the business growth, and the system stability. 3. The implementation and features of go-zero go-zero is a web and rpc framework that integrates lots of engineering practices. The features are mainly listed below: powerful tool included, less code to write simple interfaces fully compatible with net/http middlewares are supported, easy to extend high performance failure-oriented programming, resilience design builtin service discovery, load balancing builtin concurrency control, adaptive circuit breaker, adaptive load shedding, auto trigger, auto recover auto validation of API request parameters chained timeout control auto management of data caching call tracing, metrics and monitoring high concurrency protected As below, go-zero protects the system with couple layers and mechanisms: 4. Future development plans of go-zero auto generate API mock server, make the client debugging easier auto generate the simple integration test for the server side just from the .api files 5. Installation Run the following command under your project: go get -u github.com/zeromicro/go-zero 6. Quick Start full examples can be checked out from below: Rapid development of microservice systems Rapid development of microservice systems - multiple RPCs install goctl goctlcan be read as go control. goctl means not to be controlled by code, instead, we control it. The inside go is not golang. At the very beginning, I was expecting it to help us improve the productivity, and make our lives easier. GO111MODULE=on go get -u github.com/zeromicro/go-zero/tools/goctl make sure goctl is executable. create the API file, like greet.api, you can install the plugin of goctl in vs code, api syntax is supported. type Request struct { Name string `path:\"name,options=you|me\"` // parameters are auto validated } type Response struct { Message string `json:\"message\"` } service greet-api { @handler GreetHandler get /greet/from/:name(Request) returns (Response); } the .api files also can be generate by goctl, like below: goctl api -o greet.api generate the go server side code goctl api go -api greet.api -dir greet the generated files look like: ├── greet │ ├── etc │ │ └── greet-api.yaml // configuration file │ ├── greet.go // main file │ └── internal │ ├── config │ │ └── config.go // configuration definition │ ├── handler │ │ ├── greethandler.go // get/put/post/delete routes are defined here │ │ └── routes.go // routes list │ ├── logic │ │ └── greetlogic.go // request logic can be written here │ ├── svc │ │ └── servicecontext.go // service context, mysql/redis can be passed in here │ └── types │ └── types.go // request/response defined here └── greet.api // api description file the generated code can be run directly: cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml by default, it’s listening on port 8888, while it can be changed in configuration file. you can check it by curl: curl -i http://localhost:8888/greet/from/you the response looks like: HTTP/1.1 200 OK Date: Sun, 30 Aug 2020 15:32:35 GMT Content-Length: 0 Write the business logic code the dependencies can be passed into the logic within servicecontext.go, like mysql, reds etc. add the logic code in logic package according to .api file Generate code like Java, TypeScript, Dart, JavaScript etc. just from the api file goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 7. Benchmark Checkout the test code 8. Documents (adding) Rapid development of microservice systems Rapid development of microservice systems - multiple RPCs Examples 9. Important notes Use grpc 1.29.1, because etcd lib doesn’t support latter versions. google.golang.org/grpc v1.29.1 For protobuf compatibility, use protocol-gen@v1.3.2. go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 10. Chat group Join the chat via https://join.slack.com/t/go-zeroworkspace/shared_invite/zt-m39xssxc-kgIqERa7aVsujKNj~XuPKg Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"about-us.html":{"url":"about-us.html","title":"About Us","keywords":"","body":"About Us Go-Zero go-zero is a web and rpc framework that integrates various engineering practices. Through flexible design, the stability of the large concurrent server is guaranteed, and it has withstood full actual combat tests. go-zero contains a minimalist API definition and generation tool goctl, which can generate ready, runnable code for Go, iOS, Android, Kotlin, Dart, TypeScript, and JavaScript with one click, based on versatile api definition files. Go-Zero's Author kevwan is in charge of XiaoHeiBan’s R&D department and is a senior technical expert in the TAL Education Group. He has 14 years of R&D team management experience, 16 years of architecture design experience, and 20 years of practical engineering experience. He is responsible for the architecture design of many large-scale projects, and has consulted on many more. He has also lectured at the Gopher China Conference, and the Tencent Cloud Developer Conference. Go-Zero Members As of February 2021, go-zero currently has 30 team developers and 50+ community developers. Go-Zero Community go-zero has more than 3,000 community members. Here, you can discuss any go-zero technology, feedback on issues, get the latest go-zero information, and the technical experience shared by the big guys every day. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"join-us.html":{"url":"join-us.html","title":"Join Us","keywords":"","body":"Join Us Summary go-zero is an open-source project based on the MIT License. If you find bugs, have questions, or want to introduce new features, your contributions to the project are encouraged! We welcome your active participation and will respond to various questions, issues, and PRs as soon as possible. Contribution form Pull Request Issue Contribution notes The code in go-zero's Pull request needs to meet certain specifications: For naming conventions, please read the naming conventions doc. Code annotations should be in English where possible Pull Requests should describe the functional needs clearly and concisely Unit test coverage should be 80% at a minimum Pull Requests(PRs) Visit the go-zero project, and fork it to a new GitHub repository under your account Go back to your GitHub homepage and find the xx/go-zero project, where xx is your username. (e.g. anqiansong/go-zero) Clone the code to your local machine Develop your changes locally and then push them back to your GitHub repository From your account's go-zero project, click on Pull request in the branch info box For the base repository, choose zeromicro/go-zero with base:master. For the head repository choose your go-zero project (e.g. xx/go-zero) and compare:$branch with $branch being the new feature branch in your repository that you want merged back into the upstream project Click [Create pull request] To confirm whether the PR submitted successfully, enter the go-zero Pull requests page and look for your submission with your branch name Issues Our community will make every effort to actively provide feedback and support for problems encountered while using go-zero. However, due to the large number of people in the community both asking and answering questions, we make no guarantees about the order in which questions will be answered. Additionally, while our team is working on issues raised by the community, time constraints could cause some questions to go unanswered. One way to mitigate this is to post all feedback, be it regarding new features or discovered bugs, as a github issue for easier tracking. The go-zero team will also ensure that new features have their own issue created for easy reference by the community. The issues page is also where you can keep track of the latest go-zero news. We welcome everyone to actively participate in the issues page and community discussions. How to create a new issue Navigate to the go-zero issues page Click [New issue] in the upper right corner Choose the issue category from [Bug report], [Feature request], or [Question] Fill in the issue title and content Click [Submit new issue] Reference Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"Concepts","keywords":"","body":"Concepts go-zero go-zero is a batteries-included web and rpc framework that incorporates many known-good engineering practices. It was created with the goal of allowing developers to rapidly develop resilient services with rock-solid stability, and has been used to develop web applications with tens of millions users. goctl goctl is an auxiliary tool designed to improve engineering efficiency and reduce error rates for developers by reducing the amount of boilerplate necessary for services to communicate with each other. goctl plugins goctl has an extensible plugin system with multiple plugins already created. These plugins allow developers to generate boilerplate code and configuration for other architecture elements. Examples include goctl-go-compact which merges all goctl-generated routes into one file, the goctl-swagger plugin for generating swagger documents from api definitions, or the goctl-php plugin for generating php client code, etc. IntelliJ/Visual Studio Code plugins There are also plugins available for the IntelliJ family of IDEs as well as for Visual Studio Code. These plugins expose the CLI functions in the interface of the IDE and provide additional syntax highlighting for API definition files. API file An API file refers to the plaintext file used to define and describe an API service. It ends with the .api suffix and contains an IDL (Interface Description Language) describing the API syntax. goctl environment In order to use goctl the following is required: a golang environment protoc, the protobuf compiler the protoc-gen-go plugin a go project either using go modules or on the GOPATH If you don't have all of this setup, please visit the prepare of the documentation to get started. go-zero-demo Next up are some quickstart demos to get started with go-zero. Since these demos will be sizable after all the code is generated, it is recommended you create a go-zero-demo directory to house it all. See below for how to create this in your home directory. $ cd ~ $ mkdir go-zero-demo && cd go-zero-demo $ go mod init go-zero-demo Reference go-zero Goctl Plugins Tools API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"quick-start.html":{"url":"quick-start.html","title":"Quick Start","keywords":"","body":"Quick Start [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section mainly starts by quickly starting services such as api/rpc to let everyone have a macro concept of the project developed with go-zero, and we will introduce them in more detail in the follow-up. If you have already prepared the environment and tools with reference to prepare, please follow the following sections to start the experience: monolithic service micro service Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"Monolithic Service","keywords":"","body":"Monolithic Service [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward Since go-zero integrates web/rpc, some friends in the community will ask me whether go-zero is positioned as a microservice framework. The answer is no. Although go-zero integrates many functions, you can use any one of them independently, or you can develop a single service. It is not that every service must adopt the design of the microservice architecture. Create greet service $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. Take a look at the structure of the greet service $ cd greet $ tree . ├── etc │ └── greet-api.yaml ├── go.mod ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go It can be observed from the above directory structure that although the greet service is small, it has \"all internal organs\". Next, we can write business code in greetlogic.go. Write logic $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } Start and access the service Start service $ cd greet $ go run greet.go -f etc/greet-api.yaml Starting server at 0.0.0.0:8888... Access service $ curl -i -X GET http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} Source code greet source code Guess you wants Goctl API Directory Structure API IDL API Configuration Middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"micro-service.html":{"url":"micro-service.html","title":"Micro Service","keywords":"","body":"Microservices [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous article we have shown how to quickly create a single service, next we will show how to quickly create a microservice. In this section, the api part is actually the same logic as the monolithic service, except that there is no communication between services in the monolithic service, and the api service in the microservice will have more configuration for rpc calls. Preface This section will be a simple demonstration of an order service to call the user service, the demo code only to pass the idea, some of the links will not be enumerated. Scenario Summary Suppose we are developing a mall project, and the developer Xiaoming is responsible for the development of the user module (user) and the order module (order), let's split these two modules into two microservices ① [!NOTE] ①: The splitting of microservices is also a learning curve, so we won't discuss the details of how to split microservices here. Demonstrate functional goals Order service(order) provides a query interface user service (user) provides a method for the order service to obtain user information Service Design Analysis According to the scenario synopsis we can learn that the order is directly user-oriented, accessing data through the http protocol, and the order internal need to obtain some basic data about the user, since our service is designed using the microservices architecture. Then the two services (user,order) must exchange data, the data exchange between services that is, the communication between services, to here, the use of a reasonable communication protocol is also a developer needs to Here we choose rpc to realize the communication between services, and I believe I have already made a better scenario of \"What is the role of rpc service? I believe I have already described a good scenario here. Of course, there is much more to a service than just design analysis before development, so we won't go into detail here. From the above, we know that we need a user rpc order api two services to initially implement this little demo. Create mall project If you run the monolithic example, which is also called go-zero-demo, you may need to change the parent directory. $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo Note: If there is no cd operation to change the directory, all operations are performed in the `go-zero-demo directory Create user rpc service Create the user rpc service $ mkdir -p mall/user/rpc Add user.proto file, add getUser method $ vim mall/user/rpc/user.proto Add the following code. syntax = \"proto3\"; package user; // protoc-gen-go version is greater than 1.4.0, proto file needs to add go_package, otherwise it can't be generated option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // user id string id = 1; // user name string name = 2; // user gender string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } Generate the code $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. [!TIPS] grpc directive details refer to https://grpc.io/docs/languages/go/quickstart/ Padding business logic $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } Create order api service Create order api service # in dir go-zero-demo/mall $ mkdir -p order/api && cd order/api Add api file $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } Generate the order service $ goctl api go -api order.api -dir . Done. Add user rpc configuration $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } Add yaml configuration $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc refine the service dependencies $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } Adding order demo logic Add business logic to getorderlogic $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } Start the service and verify start etcd$ etcd download dependencies# in dir go-zero-demo $ go mod tidy start user rpc # in dir mall/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... start order api # in dir mall/order/api $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... Accessing the order api curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\": \"1\", \"name\": \"test order\"} [!TIP] The api syntax mentioned in the demo, rpc generation, goctl, goctl environment, etc. how to use and install, the quick start does not provide a detailed overview, we will have a detailed description of the subsequent documentation, you can also click the following [guess what you want to see] quick jump to see the corresponding documentation. Source Code mall source code Guess what you want to see goctl Usage Notes api directory structure introduction api syntax api configuration file introduction api middleware usage rpc directory rpc-config rpc caller description Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"framework-design.html":{"url":"framework-design.html","title":"Framework Design","keywords":"","body":"Framework Design [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section will explain the design of go-zero framework from the go-zero design philosophy and the best practice catalog of go-zero services. This section will contain the following subsections: Go-Zero Design Go-Zero Features API IDL API Directory Structure RPC Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"Go-Zero Design","keywords":"","body":"Go-Zero Design [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR For the design of the microservice framework, we expect that while ensuring the stability of microservices, we must also pay special attention to research and development efficiency. So at the beginning of the design, we have the following guidelines: Keep it simple, first principle Flexible design, fault-oriented programming Tools are bigger than conventions and documents High availability High concurrency Easy to expand Friendly to business development, package complexity There is only one way to constrain one thing Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"Go-Zero Features","keywords":"","body":"Go-Zero Features [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR go-zero is a web and rpc framework that integrates various engineering practices. It has the following main features: Powerful tool support, as little code writing as possible Minimalist interface Fully compatible with net.http Support middleware for easy expansion High performance Fault-oriented programming, flexible design Built-in service discovery, load balancing Built-in current limiting, fusing, load reduction, and automatic triggering, automatic recovery API parameter automatic verification Timeout cascade control Automatic cache control Link tracking, statistical alarm, etc. High concurrency support, stably guaranteeing daily traffic peaks during the epidemic As shown in the figure below, we have ensured the high availability of the overall service from multiple levels: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-grammar.html":{"url":"api-grammar.html","title":"API IDL","keywords":"","body":"API syntax [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR API IDL example /** * api syntax example and syntax description */ // api syntax version syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"anqiansong\" date: \"2020-01-08\" desc: \"api syntax example and syntax description\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } API syntax structure syntax statement import syntax block info syntax block type syntax block service syntax block hidden channel [!TIP] In the above grammatical structure, grammatically speaking, each grammar block can be declared anywhere in the .api file according to the grammatical block.> But in order to improve reading efficiency, we suggest to declare in the above order, because it may be in the future Strict mode is used to control the order of syntax blocks. syntax statement syntax is a newly added grammatical structure, the introduction of the grammar can solve: Quickly locate the problematic grammatical structure of the api version Syntax analysis for the version Prevent the big version upgrade of api syntax from causing backward compatibility **[!WARNING] The imported api must be consistent with the syntax version of the main api. Grammar definition 'syntax'={checkVersion(p)}STRING Grammar description syntax: Fixed token, marking the beginning of a syntax structure checkVersion: Custom go method to detect whether STRING is a legal version number. The current detection logic is that STRING must meet the regularity of (?m)\"v[1-9][0-9]\". STRING: A string of English double quotes, such as \"v1\" An api grammar file can only have 0 or 1 syntax statement. If there is no syntax, the default version is v1 Examples of correct syntax ✅ eg1: Irregular writing syntax=\"v1\" eg2: Standard writing (recommended) syntax = \"v2\" Examples of incorrect syntax ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" Import syntax block As the business scale increases, there are more and more structures and services defined in the api. All the grammatical descriptions are in one api file. This is a problem, and it will greatly increase the difficulty of reading and maintenance. Import The grammar block can help us solve this problem. By splitting the api file, different api files are declared according to certain rules, which can reduce the difficulty of reading and maintenance. **[!WARNING] Import here does not include package declarations like golang, it is just the introduction of a file path. After the final analysis, all the declarations will be gathered into a spec.Spec. You cannot import multiple identical paths, otherwise it will cause parsing errors. Grammar definition 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' Grammar description import: fixed token, marking the beginning of an import syntax checkImportValue: Custom go method to detect whether STRING is a legal file path. The current detection logic is that STRING must satisfy (?m)\"(?[az AZ 0-9_-])+\\. api\" regular. STRING: A string of English double quotes, such as \"foo.api\" Examples of correct syntax ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) Examples of incorrect syntax ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info syntax block The info grammar block is a grammar body that contains multiple key-value pairs. Its function is equivalent to the description of an api service. The parser will map it to spec.Spec for translation into other languages ​​(golang, java, etc.) Is the meta element that needs to be carried. If it is just a description of the current api, without considering its translation to other languages, you can use simple multi-line comments or java-style documentation comments. For comment descriptions, please refer to the hidden channels below. **[!WARNING] Duplicate keys cannot be used, each api file can only have 0 or 1 info syntax block Grammar definition 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' Grammar description info: fixed token, marking the beginning of an info syntax block checkKeyValue: Custom go method to check whether VALUE is a legal value. VALUE: The value corresponding to the key, which can be any character after a single line except'\\r','\\n',''. For multiple lines, please wrap it with \"\", but it is strongly recommended that everything be wrapped with \"\" Examples of correct syntax ✅ eg1:Irregular writing info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:Standard writing (recommended) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) Examples of incorrect syntax ❌ eg1:No key-value info() eg2:Does not contain colon info( foo value ) eg3:key-value does not wrap info(foo:\"value\") eg4:No key info( : \"value\" ) eg5:Illegal key info( 12: \"value\" ) eg6:Remove the old version of multi-line syntax info( foo: > some text type syntax block In the api service, we need to use a structure (class) as the carrier of the request body and the response body. Therefore, we need to declare some structures to accomplish this. The type syntax block evolved from the type of golang. Of course It also retains some of the characteristics of golang type, and the following golang characteristics are used: Keep the built-in data types of golang bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, Compatible with golang struct style declaration Keep golang keywords **[!WARNING]️ Does not support alias Does not support time.Time type, because client code is generated from API file, and not all client languages support time.Time related types Structure name, field name, cannot be a golang keyword Grammar definition Since it is similar to golang, it will not be explained in detail. Please refer to the typeSpec definition in ApiParser.g4 for the specific syntax definition. Grammar description Refer to golang writing Examples of correct syntax ✅ eg1:Irregular writing type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // Non-exported field bar int `form:\"bar\"` } type( // Non-derived structure fooBar struct{ FooBar int } ) eg2: Standard writing (recommended) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int } ) Examples of incorrect syntax ❌ eg type Gender int // not support // Non-struct token type Foo structure{ CreateTime time.Time // Does not support time.Time } // golang keyword var type var{} type Foo{ // golang keyword interface Foo interface } type Foo{ foo int // The map key must have the built-in data type of golang m map[Bar]string } [!NOTE] ① The tag definition is the same as the json tag syntax in golang. In addition to the json tag, go-zero also provides some other tags to describe the fields, See the table below for details. tag table When binding parameters, only one of the following four tags can be selected tag key Description ProviderEffective Coverage Example json Json serialization tag golang request、response json:\"fooo\" path Routing path, such as/foo/:id go-zero request path:\"id\" form Mark that the request body is a form (in the POST method) or a query (in the GET method)/search?name=keyword) go-zero request form:\"name\" header Parse values from HTTP headers, like Name: value go-zero request header:\"name\" tag modifier Common parameter verification description tag key Description Provider Effective Coverage Example optional Define the current field as an optional parameter go-zero request json:\"name,optional\" options Define the enumeration value of the current field, separated by a vertical bar | go-zero request json:\"gender,options=male\" default Define the default value of the current field go-zero request json:\"gender,default=male\" range Define the value range of the current field go-zero request json:\"age,range=[0:120]\" [!TIP] The tag modifier needs to be separated by a quotation comma after the tag value service syntax block The service syntax block is used to define api services, including service name, service metadata, middleware declaration, routing, handler, etc. **[!WARNING]️ The service name of the main api and the imported api must be the same, and there must be no ambiguity in the service name. The handler name cannot be repeated The name of the route (request method + request path) cannot be repeated The request body must be declared as a normal (non-pointer) struct, and the response body has been processed for forward compatibility. Please refer to the following description for details Grammar definition serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; Grammar description serviceSpec: Contains an optional syntax block atServer and serviceApi syntax block, which follow the sequence mode (the service must be written in order, otherwise it will be parsed incorrectly) atServer: Optional syntax block, defining server metadata of the key-value structure,'@server' indicates the beginning of this server syntax block, which can be used to describe serviceApi or route syntax block, and it has some special keys when it is used to describe different syntax blocks key needs attention,see atServerKey Key Description。 serviceApi: Contains one or more serviceRoute syntax blocks serviceRoute: Contains atDoc, handler and route in sequence mode atDoc: Optional syntax block, a key-value description of a route, which will be passed to the spec.Spec structure after parsing. If you don't care about passing it to spec.Spec, it is recommended to use a single-line comment instead. handler: It is a description of the handler layer of routing. You can specify the handler name by specifying the handler key by atServer, or you can directly use the atHandler syntax block to define the handler name atHandler: @handler fixed token, followed by a value following the regularity [_a-zA-Z][a-zA-Z_-], used to declare a handler name route: Routing consists of httpMethod, path, optional request, optional response, and httpMethod must be lowercase. body: api request body grammar definition, it must be an optional ID value wrapped by () replyBody: api response body grammar definition, must be a struct wrapped by ()、array(Forward compatible processing, it may be discarded in the future, it is strongly recommended to wrap it in struct instead of using array directly as the response body) kvLit: Same as info key-value serviceName: There can be multiple'-'join ID values path: The api request path must start with / or /:, and must not end with /. The middle can contain ID or multiple ID strings with - join atServerKey Key Description When modifying the service keyDescriptionExample jwtDeclare that all routes under the current service require jwt authentication, and code containing jwt logic will be automatically generatedjwt: Auth groupDeclare the current service or routing file groupgroup: login middlewareDeclare that the current service needs to open the middlewaremiddleware: AuthMiddleware prefixDeclare a route prefixprefix: /api When modifying the route keyDescriptionExample handlerDeclare a handler- Examples of correct syntax ✅ eg1:Irregular writing @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: /api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // Non-exported body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// Array is not recommended as response body @handler fooBar post /foo/bar (Foo) returns // You can omit 'returns' } eg2:Standard writing (recommended) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: /api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } Examples of incorrect syntax ❌ // Empty server syntax block is not supported @server( ) // Empty service syntax block is not supported service foo-api{ } service foo-api{ @doc kkkk // The short version of the doc must be enclosed in English double quotation marks @handler foo post /foo @handler foo // Duplicate handler post /bar @handler fooBar post /bar // Duplicate routing // @handler and @doc are in the wrong order @handler someHandler @doc \"some doc\" post /some/path // handler is missing post /some/path/:id @handler reqTest post /foo/req (*Foo) // Data types other than ordinary structures are not supported as the request body @handler replyTest post /foo/reply returns (*Foo) // Does not support data types other than ordinary structures and arrays (forward compatibility, later considered to be discarded) as response bodies } Hidden channel Hidden channels are currently mainly blank symbols, newline symbols and comments. Here we only talk about comments, because blank symbols and newline symbols are currently useless. Single line comment Grammar definition '//' ~[\\r\\n]* Grammar description It can be known from the grammatical definition that single-line comments must start with //, and the content cannot contain newline characters Examples of correct syntax ✅ // doc // comment Examples of incorrect syntax ❌ // break line comments java style documentation comments Grammar definition '/*' .*? '*/' Grammar description It can be known from the grammar definition that a single line comment must start with any character that starts with /* and ends with */. Examples of correct syntax ✅ /** * java-style doc */ Examples of incorrect syntax ❌ /* * java-style doc */ */ Doc&Comment If you want to get the doc or comment of a certain element, how do you define it? Doc We stipulate that the number of lines in the previous grammar block (non-hidden channel content) line+1 to all comments (the current line, or multiple lines) before the first element of the current grammar block are doc, And retain the original mark of //, /*, */. Comment We specify that a comment block (the current line, or multiple lines) at the beginning of the line where the last element of the current syntax block is located is comment, And retain the original mark of //, /*, */. Syntax block Doc and Comment support situation Syntax blockParent Syntax BlockDocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ The following is the writing of doc and comment after the corresponding syntax block is parsed // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * Enable jwt authentication */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * Route doc * Post request * Route path: foo * Request body: Foo * Response body: Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-dir.html":{"url":"api-dir.html","title":"API Directory Structure","keywords":"","body":"API directory introduction [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR . ├── etc │ └── greet-api.yaml // yaml configuration file ├── go.mod // go module file ├── greet.api // api interface description language file ├── greet.go // main function entry └── internal ├── config │ └── config.go // configuration declaration type ├── handler // routing and handler forwarding │ ├── greethandler.go │ └── routes.go ├── logic // business logic │ └── greetlogic.go ├── middleware // middleware file │ └── greetmiddleware.go ├── svc // the resource pool that logic depends on │ └── servicecontext.go └── types // The struct of request and response is automatically generated according to the api, and editing is not recommended └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"RPC Directory Structure","keywords":"","body":"RPC Directory Structure [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR proto greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [Goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations, your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. dir structure: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] The directory where the pb.go & _grpc.pb.go files are located is not fixed, it is determined by go_opt & go-grpc_opt in conjunction with the go_package value in the proto file, to understand the logic of the grpc code generation directory read Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"project-dev.html":{"url":"project-dev.html","title":"Project Development","keywords":"","body":"Project Development [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous chapters, we have introduced go-zero from the dimensions of some concepts, backgrounds, and quick start. Seeing this, I believe you already have some understanding of go-zero. From here, we will start to explain the entire process from environment preparation to service deployment. In order to ensure that everyone can thoroughly understand the go-zero development process, then prepare your patience and move on. In the chapters, the following subsections will be included: Prepare Golang Installation Go Module Configuration Goctl Installation protoc & protoc-gen-go Installation More Development Rules Naming Rules Route Rules Coding Rules Development Flow Configuration Introduction API Configuration RPC Configuration Business Development Directory Structure Model Generation API Coding Business Coding JWT Middleware RPC Implement & Call Error Handling CI/CD Service Deployment Log Collection Trace Monitor Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare.html":{"url":"prepare.html","title":"Prepare","keywords":"","body":"Prepare [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before officially entering the actual development, we need to do some preparations, such as: installation of the Go environment, installation of tools used for grpc code generation, Installation of the necessary tool Goctl, Golang environment configuration, etc., this section will contain the following subsections: Golang Installation Go Module Configuration Goctl Installation protoc & protoc-gen-go Installation More Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"golang-install.html":{"url":"golang-install.html","title":"Golang Installation","keywords":"","body":"Golang Installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward To develop a golang program, the installation of its environment must be indispensable. Here we choose to take 1.15.1 as an example. Official document https://golang.google.cn/doc/install Install Go on macOS Download and install Go for Mac Verify the installation result $ go version go version go1.15.1 darwin/amd64 Install Go on linux Download Go for Linux Unzip the compressed package to /usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz Add /usr/local/go/bin to environment variables $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile Verify the installation result $ go version go version go1.15.1 linux/amd64 Install Go on windows Download and install Go for Windows Verify the installation result $ go version go version go1.15.1 windows/amd64 More For more operating system installation, see https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gomod-config.html":{"url":"gomod-config.html","title":"Go Module Configuration","keywords":"","body":"Go Module Configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Introduction to Go Module Modules are how Go manages dependencies.[1] That is, Go Module is a way for Golang to manage dependencies, similar to Maven in Java and Gradle in Android. Module configuration Check the status of GO111MODULE $ go env GO111MODULE on Turn on GO111MODULE, if it is already turned on (that is, execute go env GO111MODULE and the result is on), please skip it. $ go env -w GO111MODULE=\"on\" Set up GOPROXY $ go env -w GOPROXY=https://goproxy.cn Set up GOMODCACHE view GOMODCACHE $ go env GOMODCACHE If the directory is not empty or /dev/null, please skip it. go env -w GOMODCACHE=$GOPATH/pkg/mod Reference [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-install.html":{"url":"goctl-install.html","title":"Goctl Installation","keywords":"","body":"Goctl Installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Foreword Goctl plays a very important role in the development of the go-zero project. It can effectively help developers greatly improve development efficiency, reduce code error rate, and shorten the workload of business development. For more introductions to Goctl, please read Goctl Introduction , Here we strongly recommend that you install it, because most of the follow-up demonstration examples will use goctl for demonstration. Install(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 go install github.com/zeromicro/go-zero/tools/goctl@latest Environmental variable detection The compiled binary file downloaded by go get is located in the $GOPATH/bin directory. Make sure that $GOPATH/bin has been added to the environment variable. $ sudo vim /etc/paths Add the following in the last line $GOPATH/bin [!TIP] $GOPATH is the filepath on your local machine Installation result verification $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] For windows users to add environment variables, please Google by yourself. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go Installation","keywords":"","body":"protoc & protoc-gen-go installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward protoc is a tool written in C++, which can translate proto files into codes in the specified language. In the go-zero microservices, we use grpc to communicate between services, and the writing of grpc requires the use of protoc and the plug-in protoc-gen-go that translates into go language rpc stub code. Demonstration environment of this document mac OS way 1: install from goctl $ goctl env check -i -f -v [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! way2: install from web page protoc installation Enter the protobuf release page and select the compressed package file suitable for your operating system Unzip protoc-3.14.0-osx-x86_64.zip and enter protoc-3.14.0-osx-x86_64 $ cd protoc-3.14.0-osx-x86_64/bin Move the started protoc binary file to any path added to the environment variable, such as $GOPATH/bin. It is not recommended putting it directly with the next path of the system. $ mv protoc $GOPATH/bin [!TIP] $GOPATH is the actual folder address of your local machine Verify the installation result $ protoc --version libprotoc 3.14.0 protoc-gen-* installation [!TIPS] Windows may report an error, A required privilege is not held by the client., because goctl needs to be run as administrator under Windows. The reason is that goctl needs to be run \"as administrator\" under Windows. Download and install protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest **[!WARNING] protoc-gen-go installation failed, please read Error Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare-other.html":{"url":"prepare-other.html","title":"More","keywords":"","body":"Other [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before, we have prepared the Go environment, Go Module configuration, Goctl, protoc & protoc-gen-go installation, these are the environments that developers must prepare during the development phase, and you can optionally install the next environment, Because these environments generally exist on the server (installation work, operation and maintenance will be completed for you), but in order to complete the follow-up demonstration process, I suggest you install it locally, because most of our demo environments will be Locally based. The following only gives the necessary preparatory work, and does not give a detailed introduction in the length of the document. Other environment etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-specification.html":{"url":"dev-specification.html","title":"Development Rules","keywords":"","body":"Development Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In actual business development, in addition to improving business development efficiency, shortening business development cycles, and ensuring high performance and high availability indicators for online business, good programming habits are also one of the basic qualities of a developer. In this chapter, We will introduce the coding standards in go-zero. This chapter is an optional chapter. The content is for communication and reference only. This chapter will explain from the following subsections: Naming Rules Route Rules Coding Rules Three principles of development Clarity The author quoted a quote from Hal Abelson and Gerald Sussman: Programs must be written for people to read, and only incidentally for machines to execute Simplicity Simplicity is prerequisite for reliability Edsger W. Dijkstra believes that: the prerequisite for reliability is simplicity. We have all encountered in actual development. What is this code written and what it wants to accomplish. Developers don’t understand this code, so they don’t know. How to maintain, this brings complexity, the more complex the program, the harder it is to maintain, and the harder it is to maintain, the program becomes more and more complicated. Therefore, the first thing you should think of when encountering a program becoming complicated is - Refactoring, refactoring will redesign the program and make the program simple. Productivity) In the go-zero team, this topic has always been emphasized. The productivity of developers is not how many lines of code you have written and how many module developments you have completed, but we need to use various effective ways to take advantage of the limited Time to complete the development to maximize the efficiency, and the birth of Goctl was officially to increase productivity, Therefore, I very much agree with this development principle. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"naming-spec.html":{"url":"naming-spec.html","title":"Naming Rules","keywords":"","body":"Naming Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In any language development, there are some naming conventions in the language field, good Reduce code reading costs Reduce maintenance difficulty Reduce code complexity Specification suggestion In our actual development, many developers may transfer from one language to another language field. After switching to another language, We will all retain the programming habits of the old language. Here, what I suggest is that although some previous specifications of different languages may be the same, But we'd better be familiar with some official demos to gradually adapt to the programming specifications of the current language, rather than directly migrating the programming specifications of the original language. Naming guidelines When the distance between the definition and the last use of the variable name is short, the short name looks better. Variable naming should try to describe its content, not type Constant naming should try to describe its value, not how to use this value When encountering for, if and other loops or branches, single letter names are recommended to identify parameters and return values It is recommended to use words to name method, interface, type, and package The package name is also part of the naming, please use it as much as possible Use a consistent naming style File naming guidelines All lowercase Avoid underscores (_) except for unit test The file name should not be too long Variable naming convention reference Initial lowercase Hump naming See the name to know the meaning, avoid pinyin instead of English It is not recommended including an underscore (_) It is not recommended including numbers Scope of application Local variables Function parameter output, input parameter Function and constant naming convention Camel case naming The first letter of the exported must be capitalized The first letter must be lowercase if it cannot be exported Avoid the combination of all uppercase and underscore (_) [!TIP] If it is a go-zero code contribution, you must strictly follow this naming convention Reference Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"Route Rules","keywords":"","body":"Route Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Recommended spine naming Combinations of lowercase words and horizontal bars (-) What you see is what you get /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"coding-spec.html":{"url":"coding-spec.html","title":"Coding Rules","keywords":"","body":"Coding Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR import Single-line import is not recommended being wrapped in parentheses Introduce in the order of Official Package, NEW LINE, Project Package, NEW LINE, Third Party Dependent Package import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) Function returns Object avoids non-pointer return Follow the principle that if there is a normal value return, there must be no error, and if there is an error, there must be no normal value return. Error handling An error must be handled, if it cannot be handled, it must be thrown. Avoid underscore (_) receiving error Function body coding It is recommended that a block end with a blank line, such as if, for, etc. func main (){ if x==1{ // do something } fmt.println(\"xxx\") } Blank line before return func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-flow.html":{"url":"dev-flow.html","title":"Development Flow","keywords":"","body":"Development Flow [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The development process here is not a concept with our actual business development process. The definition here is limited to the use of go-zero, that is, the development details at the code level. Development Flow Goctl environment preparation [1] Database Design Business development New Construction Create service catalog Create service type (api/rpc/rmq/job/script) Write api and proto files Code generation Generate database access layer code model Configuration config, yaml change Resource dependency filling (ServiceContext) Add middleware Business code filling Error handling [!TIP] [1] goctl environment Development Tools Visual Studio Code Goland (recommended) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"config-introduction.html":{"url":"config-introduction.html","title":"Configuration Introduction","keywords":"","body":"Configuration Introduction [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before officially using go-zero, let us first understand the configuration definitions of different service types in go-zero, and see what role each field in the configuration has. This section will contain the following subsections: API Configuration RPC Configuration Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-config.html":{"url":"api-config.html","title":"API Configuration","keywords":"","body":"API configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The api configuration controls various functions in the api service, including but not limited to the service listening address, port, environment configuration, log configuration, etc. Let's take a simple configuration to see what the common configurations in the api do. Configuration instructions Through the yaml configuration, we will find that there are many parameters that we are not aligned with config. This is because many of the config definitions are labeled with optional or default. For optional options, you can choose according to your own Need to determine whether it needs to be set. For the default tag, if you think the default value is enough, you don't need to set it. Generally, the value in default basically does not need to be modified and can be considered as a best practice value. Config type Config struct{ rest.RestConf // rest api configuration Auth struct { // jwt authentication configuration AccessSecret string // jwt key AccessExpire int64 // jwt expire, unit: second } Mysql struct { // database configuration, in addition to mysql, there may be other databases such as mongo DataSource string // mysql datasource, which satisfies the format of user:password@tcp(ip:port)db?queries } CacheRedis cache.CacheConf // redis cache UserRpc zrpc.RpcClientConf // rpc client configuration } rest.RestConf The basic configuration of api service, including monitoring address, monitoring port, certificate configuration, current limit, fusing parameters, timeout parameters and other controls, expand it, we can see: service.ServiceConf // service configuration Host string `json:\",default=0.0.0.0\"` // http listening ip, default 0.0.0.0 Port int // http listening port, required CertFile string `json:\",optional\"` // https certificate file, optional KeyFile string `json:\",optional\"` // https private key file, optional Verbose bool `json:\",optional\"` // whether to print detailed http request log MaxConns int `json:\",default=10000\"` // http can accept the maximum number of requests at the same time (current limit), the default is 10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http can accept the maximum Content Length of the request, the default is 1048576, and the set value cannot be between 0 and 8388608 // milliseconds Timeout int64 `json:\",default=3000\"` // timeout duration control, unit: milliseconds, default 3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // CPU load reduction threshold, the default is 900, the allowable setting range is 0 to 1000 Signature SignatureConf `json:\",optional\"` // signature configuration service.ServiceConf type ServiceConf struct { Name string // service name Log logx.LogConf // log configuration Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // service environment, dev-development environment, test-test environment, pre-pre-release environment, pro-formal environment MetricsUrl string `json:\",optional\"` // index report interface address, this address needs to support post json Prometheus prometheus.Config `json:\",optional\"` // prometheus configuration } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // service name Mode string `json:\",default=console,options=console|file|volume\"` // Log mode, console-output to console, file-output to the current server (container) file, volume-output docker hangs in the file Path string `json:\",default=logs\"` // Log storage path Level string `json:\",default=info,options=info|error|severe\"` // Log level Compress bool `json:\",optional\"` // whether to enable gzip compression KeepDays int `json:\",optional\"` // log retention days StackCooldownMillis int `json:\",default=100\"` // log write interval } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus monitor host Port int `json:\",default=9101\"` // prometheus listening port Path string `json:\",default=/metrics\"` // report address } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // Whether it is Strict mode, if it is, Private Keys is required Expiry time.Duration `json:\",default=1h\"` // Validity period, default is 1 hour PrivateKeys []PrivateKeyConf // Signing key related configuration } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // Fingerprint configuration KeyFile string // Key configuration } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // Weights } redis.RedisConf RedisConf struct { Host string // redis address Type string `json:\",default=node,options=node|cluster\"` // redis type Pass string `json:\",optional\"` // redis password } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-config.html":{"url":"rpc-config.html","title":"RPC Configuration","keywords":"","body":"RPC Configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The rpc configuration controls various functions of an rpc service, including but not limited to listening address, etcd configuration, timeout, fuse configuration, etc. Below we will use a common rpc service configuration to illustrate. Configuration instructions Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // Redis cache configuration, see the api configuration instructions for details, and I won’t go into details here Mysql struct { // mysql database access configuration, see api configuration instructions for details, not repeat here DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // mysql database access configuration, see api configuration instructions for details, not repeat here ListenOn string // rpc listening address and port, such as: 127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd related configuration Auth bool `json:\",optional\"` // Whether to enable Auth, if yes, Redis is required Redis redis.RedisKeyConf `json:\",optional\"` // Auth verification StrictControl bool `json:\",optional\"` // Whether it is Strict mode, if it is, the error is Auth failure, otherwise it can be considered as successful // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // Timeout control, unit: milliseconds CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // CPU load reduction threshold, the default is 900, the allowable setting range is 0 to 1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host array Key string // rpc registration key } redis.RedisKeyConf RedisConf struct { Host string // redis host Type string `json:\",default=node,options=node|cluster\"` // redis type Pass string `json:\",optional\"` // redis password } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // Verification key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-dev.html":{"url":"business-dev.html","title":"Business Development","keywords":"","body":"Business development [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In this chapter, we use a simple example to demonstrate some basic functions in go-zero. This section will contain the following subsections: Directory Structure Model Generation API Coding Business Coding JWT Middleware RPC Implement & Call Error Handling Demo project download Before officially entering the follow-up document description, you can pay attention to the source code here, and we will perform a progressive demonstration of the function based on this source code. Instead of starting from 0 completely, if you come from the Quick Start chapter, this source code structure is not a problem for you. Click Here to download Demo project Demonstration project description Scenes The programmer Xiao Ming needs to borrow a copy of \"Journey to the West\". When there is no online library management system, he goes to the front desk of the library to consult with the librarian every day. Xiao Ming: Hello, do you still have the book \"Journey to the West\" today? Administrator: No more, let's check again tomorrow. One day later, Xiao Ming came to the library again and asked: Xiao Ming: Hello, do you still have the book \"Journey to the West\" today? Administrator: No, you can check again in two days. After many repetitions in this way, Xiao Ming was also in vain and wasted a lot of time on the way back and forth, so he finally couldn't stand the backward library management system. He decided to build a book review system by himself. Expected achievement User login: Rely on existing student system data to log in Book search: Search for books based on book keywords and query the remaining number of books. System analysis Service design user api: provides user login protocol rpc: for search service to access user data search api: provide book query agreement [!TIP] Although this tiny book borrowing query system is small, it does not fit the business scenario in practice, but only the above two functions have already met our demonstration of the go-zero api/rpc scenario. In order to satisfy the richer go-zero function demonstration in the future, business insertion, that is, related function descriptions, will be carried out in the document. Here only one scene is used for introduction. NOTE: Please create the sql statement in the user into the db by yourself, see prepare for more preparation work Add some preset user data to the database for later use. For the sake of space, the demonstration project does not demonstrate the operation of inserting data in detail. Reference preset data INSERT INTO `user` (number,name,password,gender)values ('666','xiaoming','123456','male'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-design.html":{"url":"service-design.html","title":"Directory Structure","keywords":"","body":"Directory Structure [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Directory splitting refers to the directory splitting in line with the best practices of go-zero, which is related to the splitting of microservices. In the best practice within the team, We split a system into multiple subsystems according to the horizontal division of the business, and each subsystem should have an independent persistent storage and cache system. For example, a shopping mall system needs to consist of a user system (user), a product management system (product), an order system (order), a shopping cart system (cart), a settlement center system (pay), an after-sale system (afterSale), etc. System structure analysis In the mall system mentioned above, while each system provides services to the outside (http), it also provides data to other subsystems for data access interfaces (rpc), so each subsystem can be split into a service , And provides two external ways to access the system, api and rpc, therefore, The above system is divided into the following structure according to the directory structure: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc call chain suggestion When designing the system, try to make the call between services one-way in the chain instead of cyclically. For example, the order service calls the user service, and the user service does not call the order service in turn. When one of the services fails to start, it will affect each other and enter an infinite loop. You order to think it is caused by the user service failure, and the user thinks it is caused by the order service. If there are a large number of services in a mutual call chain, You need to consider whether the service split is reasonable. Directory structure of common service types Among the above services, only api/rpc services are listed. In addition, there may be more service types under one service, such as rmq (message processing system), cron (timed task system), script (script), etc. , Therefore, a service may contain the following directory structure: user ├── api // http access service, business requirement realization ├── cronjob // Timed task, timed data update service ├── rmq // Message processing system: mq and dq, handle some high concurrency and delay message services ├── rpc // rpc service to provide basic data access to other subsystems └── script // Script, handle some temporary operation requirements, and repair temporary data Example of complete project directory structure mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script Guess you wants API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"model-gen.html":{"url":"model-gen.html","title":"Model Generation","keywords":"","body":"Model Generation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR First, after downloading the demo project, we will use the user's model to demonstrate the code generation. Forward Model is a bridge for services to access the persistent data layer. The persistent data of the business often exists in databases such as mysql and mongo. We all know that the operation of a database is nothing more than CURD. And these tasks will also take up part of the time for development. I once wrote 40 model files when writing a business. According to the complexity of different business requirements, on average, each model file is almost required. 10 minutes, for 40 files, 400 minutes of working time, almost a day's workload, and the goctl tool can complete the 400 minutes of work in 10 seconds. Prepare Enter the demo project book, find theuser.sql file under user/model, and execute the table creation in your own database. Code generation (with cache) The way one(ddl) Enter the service/user/model directory and execute the command $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. The way two(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource is the database connection address The way three(intellij plugin) In Goland, right-click user.sql, enter and click New->Go Zero->Model Code to generate it, or open the user.sql file, Enter the editing area, use the shortcut key Command+N (for macOS) or alt+insert (for windows), select Mode Code. [!TIP] The intellij plug-in generation needs to install the goctl plug-in, see intellij plugin for details Verify the generated model file view tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go More For persisting data, if more flexible database capabilities are required, including transactional capabilities, see Mysql For distributed transaction capabilities, see distributed-transaction.md Guess you wants Model Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-coding.html":{"url":"api-coding.html","title":"API Coding","keywords":"","body":"API File Coding [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Create file $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } Generate api service By goctl executable file $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. By Intellij Plugin Right-click on the user.api file, and then click to enter New->Go Zero->Api Code, enter the target directory selection, that is, the target storage directory of the api source code, the default is the directory where user.api is located, select Click OK after finishing the list. By Keymap Open user.api, enter the editing area, use the shortcut key Command+N (for macOS) or alt+insert (for windows), select Api Code, and also enter the directory selection pop-up window, after selecting the directory Just click OK. Guess you wants API IDL API Commands API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-coding.html":{"url":"business-coding.html","title":"Business Coding","keywords":"","body":"Business code [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous section, we have written user.api based on the preliminary requirements to describe which services the user service provides to the outside world. In this section, we will continue with the previous steps. Use business coding to tell how go-zero is used in actual business. Add Mysql configuration $ vim service/user/api/internal/config/config.go package config import \"github.com/zeromicro/go-zero/rest\" type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } Improve yaml configuration $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql database user $password: mysql database password $url: mysql database connection address $db: mysql database db name, that is, the database where the user table is located $host: Redis connection address Format: ip:port, such as: 127.0.0.1:6379 $pass: redis password For more configuration information, please refer to api configuration introduction Improve service dependence $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } Fill in the login logic $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"Invalid parameter\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"Username does not exist\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"User password is incorrect\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] For the code implementation of [start]-[end] in the above code, please refer to the Jwt Authentication chapter Guess you wants API IDL API Commands API Directory Structure JWT API Configuration Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"jwt.html":{"url":"jwt.html","title":"JWT","keywords":"","body":"JWT authentication [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Summary JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and independent method for securely transmitting information as JSON objects between parties. Since this information is digitally signed, it can be verified and trusted. The JWT can be signed using a secret (using the HMAC algorithm) or using a public/private key pair of RSA or ECDSA. When should you use JSON Web Tokens? Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains. Information exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with. Why should we use JSON Web Tokens? As JSON is less verbose than XML, when it is encoded its size is also smaller, making JWT more compact than SAML. This makes JWT a good choice to be passed in HTML and HTTP environments. Security-wise, SWT can only be symmetrically signed by a shared secret using the HMAC algorithm. However, JWT and SAML tokens can use a public/private key pair in the form of a X.509 certificate for signing. Signing XML with XML Digital Signature without introducing obscure security holes is very difficult when compared to the simplicity of signing JSON. JSON parsers are common in most programming languages because they map directly to objects. Conversely, XML doesn't have a natural document-to-object mapping. This makes it easier to work with JWT than SAML assertions. Regarding usage, JWT is used at Internet scale. This highlights the ease of client-side processing of the JSON Web token on multiple platforms, especially mobile. [!TIP] All the above content quote from jwt.io How to use jwt in go-zero Jwt authentication is generally used at the api layer. In this demonstration project, we generate jwt token when user api logs in, and verify the user jwt token when searching api for books. user api generates jwt token Following the content of the Business Coding chapter, we perfect the getJwtToken method left over from the previous section, that is, generate the jwt token logic Add configuration definition and yaml configuration items $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret: The easiest way to generate the key of the jwt token is to use an uuid value. $AccessExpire: Jwt token validity period, unit: second For more configuration information, please refer to API Configuration $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search.api uses jwt token authentication Write search.api file $ vim service/search/api/search.api type ( SearchReq { Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth: Enable jwt authentication If the routing requires JWT authentication, you need to declare this syntax flag above the service, such as /search/do above Routes that do not require jwt authentication do not need to be declared, such as /search/ping above For more grammar, please read API IDL Generate code As described above, there are three ways to generate code, so I won’t go into details here. Add yaml configuration items $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret: This value must be consistent with the one declared in the user api. $AccessExpire: Valid period Modify the port here to avoid conflicts with user api port 8888 Verify jwt token Start user api service, and login $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"xiaoming\",\"gender\":\"male\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} Start the search api service, call /search/do to verify whether the jwt authentication is passed $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... Let’s not pass the jwt token and see the result: $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 Obviously, the jwt authentication failed, and the statusCode of 401 is returned. Next, let's take a jwt token (that is, the accessToken returned by the user login) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] Service startup error, please check Error At this point, the demonstration of jwt from generation to use is complete. The authentication of jwt token is already encapsulated in go-zero. You only need to simply declare it when defining the service in the api file. Get the information carried in the jwt token After go-zero is parsed from the jwt token, the kv passed in when the user generates the token will be placed in the Context of http.Request intact, so we can get the value you want through the Context. $ vim /service/search/api/internal/logic/searchlogic.go Add a log to output the userId parsed from jwt. func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } Output {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} Guess you wants JWT API Configuration API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"middleware.html":{"url":"middleware.html","title":"Middleware","keywords":"","body":"Middleware [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous section, we demonstrated how to use jwt authentication. I believe you have mastered the basic use of jwt. In this section, let’s take a look at how to use api service middleware. Middleware classification In go-zero, middleware can be divided into routing middleware and global middleware. Routing middleware refers to certain specific routes that need to implement middleware logic, which is similar to jwt and does not place the routes under jwt:xxx Does not use middleware functions, The service scope of global middleware is the entire service. Middleware use Here we take the search service as an example to demonstrate the use of middleware Routing middleware Rewrite the search.api file and add the middleware declaration $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // Routing middleware declaration ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } Regenerate the api code $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. After the generation is completed, there will be an additional middleware directory under the internal directory, which is the middleware file, and the implementation logic of the subsequent middleware is also written here. Improve resource dependency ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } Write middleware logic Only one line of log is added here, with the content example middle. If the service runs and outputs example middle, it means that the middleware is in use. $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } Start service verification {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} Global middleware call rest.Server.Use func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // Global middleware server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} Call another service within the middleware Pass another service into the middleware by closure, example as below: // simulated another service type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // regular middleware func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // the middleware that calls another service func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } For full example, see: https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-call.html":{"url":"rpc-call.html","title":"RPC Implement & Call","keywords":"","body":"RPC Implement & Call [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In a large system, there must be data transfer between multiple subsystems (services). If there is data transfer, you need a communication method. You can choose the simplest http for communication or rpc service for communication. In go-zero, we use zrpc to communicate between services, which is based on grpc. Scenes In the front, we have improved the interface protocol for user login, user query of books, etc., but the user did not do any user verification when querying the book. If the current user is a non-existent user, we do not allow him to view book information. From the above information, we can know that the user service needs to provide a method to obtain user information for use by the search service, so we need to create a user rpc service and provide a getUser method. rpc service writing Compile the proto file $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } Generate rpc service code$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] If the installed version of protoc-gen-go is larger than 1.4.0, it is recommended to add go_package to the proto file Add configuration and improve yaml configuration items $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql database user $password: mysql database password $url: mysql database connection address $db: mysql database db name, that is, the database where the user table is located $host: Redis connection address Format: ip:port, such as: 127.0.0.1:6379 $pass: redis password $etcdHost: etcd connection address, format: ip:port, such as: 127.0.0.1:2379 For more configuration information, please refer to rpc configuration introduction Add resource dependency $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } Add rpc logic $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } Use rpc Next we call user rpc in the search service Add UserRpc configuration and yaml configuration items $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret: This value must be consistent with the one declared in the user api. $AccessExpire: Valid period $etcdHost: etcd connection address The Key in etcd must be consistent with the Key in the user rpc service configuration Add dependency $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } Supplementary logic $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // use user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } Start and verify the service Start etcd, redis, mysql Start user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... Start search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml Verification Service $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"xiyouji\",\"count\":100} Guess you wants RPC Configuration RPC Directory Structure RPC Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error-handle.html":{"url":"error-handle.html","title":"Error Handling","keywords":"","body":"Error Handling [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Error handling is an indispensable part of service. In normal business development, we can think that the http status code is not in the 2xx series, it can be regarded as an http request error. It is accompanied by error messages in response, but these error messages are all returned in plain text. In addition, I will define some business errors in the business, and the common practice is to pass The two fields code and msg are used to describe the business processing results, and it is hoped that the response can be made with the json response body. Business error response format Business processing is normal { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } Business processing exception { \"code\": 10001, \"msg\": \"something wrong\" } login of user api Previously, when we handled the login logic when the username did not exist, an error was directly returned. Let's log in and pass a username that does not exist to see the effect. curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 Username does not exist Next we will return it in json format Custom error First add a baseerror.go file in common and fill in the code $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } Replace errors in login logic with CodeError custom errors if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"Invalid parameter\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"Username does not exist\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"User password is incorrect\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil Use custom errors $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // Custom error httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } Restart service verification $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"Username does not exist\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In software engineering, CI/CD or CICD generally refers to the combined practices of continuous integration and either continuous delivery or continuous deployment. ——Wikipedia What can CI do? In modern application development, the goal is to have multiple developers working simultaneously on different features of the same app. However, if an organization is set up to merge all branching source code together on one day (known as “merge day”), the resulting work can be tedious, manual, and time-intensive. That’s because when a developer working in isolation makes a change to an application, there’s a chance it will conflict with different changes being simultaneously made by other developers. This problem can be further compounded if each developer has customized their own local integrated development environment (IDE), rather than the team agreeing on one cloud-based IDE. ——Continuous integration From a conceptual point of view, CI/CD includes the deployment process. Here, we will put the deployment (CD) in a separate section Service Deployment, This section uses gitlab to do a simple CI (Run Unit Test) demonstration. Gitlab CI Gitlab CI/CD is a built-in software development tool of Gitlab, providing Continuous Integration (CI) Continuous Delivery (CD) Continuous deployment (CD) Prepare gitlab installation git installation gitlab runner installation Enable Gitlab CI Upload code Create a new warehouse go-zero-demo in gitlab Upload the local code to the go-zero-demo warehouse Create a .gitlab-ci.yaml file in the project root directory. Through this file, a pipeline can be created, which will be run when there is a content change in the code repository. The pipeline is run in sequence by one or more. Each stage can contain one or more jobs running in parallel. Add CI content (for reference only) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] The above CI is a simple demonstration. For detailed gitlab CI, please refer to the official gitlab documentation for richer CI integration. Reference CI/CD Wikipedia Continuous integration Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-deployment.html":{"url":"service-deployment.html","title":"Service Deployment","keywords":"","body":"Service Deployment [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section uses jenkins to demonstrate a simple service deployment to k8s. Prepare k8s cluster installation gitlab environment installation jenkins environment installation redis&mysql&nginx&etcd installation goctl install [!TIP] Ensure that goctl is installed on each node of k8s Please google for the installation of the above environment, and I will not introduce it here. Service deployment 1、Relevant preparations for gitlab code warehouse 1.1、Add SSH Key Enter gitlab, click on the user center, find Settings, find the SSH Keys tab on the left 1、View the public key on the machine where jenkins is located $ cat ~/.ssh/id_rsa.pub 2、If not, you need to generate it, if it exists, please skip to step 3 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 After completing the generation, repeat the first step 3、Add the public key to gitlab 1.2、Upload the code to the gitlab warehouse Create a new project go-zero-demo and upload the code. Details are not described here. 2、jenkins 2.1、Add credentials View the private key of the machine where Jenkins is located, which corresponds to the previous gitlab public key $ cat id_rsa Enter jenkins, click on Manage Jenkins-> Manage Credentials Go to the Global Credentials page, add credentials, Username is an identifier, add pipeline later, you know that this identifier represents the credentials of gitlab, and Private Key` is the private key obtained above 2.2、 Add global variables Enter Manage Jenkins->Configure System, slide to the entry of Global Properties, add docker private warehouse related information, as shown in the figure is docker username, docker user password, docker private warehouse address [!TIP] docker_user your docker username docker_pass your docker user password docker_server your docker server The private warehouse I use here, if you don’t use the private warehouse provided by the cloud vendor, you can build a private warehouse yourself. I won’t go into details here, and you can google it yourself. 2.3、Configure git Go to Manage Jenkins->Global Tool Configureation, find the Git entry, fill in the path of the git executable file of the machine where jenkins is located, if not, you need to download the Git plugin in the jenkins plugin management. 2.4、 Add a pipeline The pipeline is used to build the project, pull code from gitlab->generate Dockerfile->deploy to k8s are all done in this step, here is the demo environment, in order to ensure the smooth deployment process, Need to install jenkins on the machine where one of the nodes of the k8s cluster is located, I installed it on the master here. Get the credential id Go to the credential page and find the credential id whose Username is gitlab Go to the jenkins homepage, click on New Item, the name is user View project git address Add the service type Choice Parameter, check This project is parameterized in General, Click Add parameter and select Choice Parameter, add the selected value constant (api, rpc) and the variable (type) of the received value according to the figure, which will be used in the Pipeline script later. Configure user, on the user configuration page, swipe down to find Pipeline script, fill in the script content pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: 'Select the branch' } stages { stage('service info') { steps { sh 'echo branch: $branch' sh 'echo build service type:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('get commit_id') { steps { echo 'get commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl version detection') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo image:${image}' sh 'docker build -t ${image} .' } } stage('Upload to the mirror warehouse') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('Deploy to k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId} should be replaced with your specific credential value, that is, a string of strings in the [Add Credentials] module, ${gitUrl} needs to be replaced with the git warehouse address of your code, other variables in the form of ${xxx} are not required Modify it and keep it as it is. port.sh case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac The content of dockerlogin.sh #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker login username $docker-pass: docker login user password $docker-server: docker private address View pipeline View k8s service Guess you wants Goctl Installation k8s docker jenkins jenkins pipeline nginx etcd Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"log-collection.html":{"url":"log-collection.html","title":"Log Collection","keywords":"","body":"Log Collection [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In order to ensure the stable operation of the business and predict the unhealthy risks of the service, the collection of logs can help us observe the current health of the service. In traditional business development, when there are not many machine deployments, we usually log in directly to the server to view and debug logs. However, as the business increases, services continue to be split. The maintenance cost of the service will also become more and more complicated. In a distributed system, there are more server machines, and the service is distributed on different servers. When problems are encountered, We can't use traditional methods to log in to the server for log investigation and debugging. The complexity can be imagined. [!TIP] If it is a simple single service system, or the service is too small, it is not recommended to use it directly, otherwise it will be counterproductive. Prepare kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash Filebeat $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # Turn on json parsing json.keys_under_root: true json.add_error_key: true # Log file path paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # Define kafka topic field fields: log_topic: log-collection # Export to kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx is the path where filebeat.yaml is located go-stash configuration Create a new config.yaml file Add configuration content $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC Start services (start in order) Start kafka Start elasticsearch Start kibana Start go-stash Start filebeat Start the order-api service and its dependent services (order-api service in the go-zero-demo project) Visit kibana Enter 127.0.0.1:5601 [!TIP] Here we only demonstrate the logs generated by logx in the collection service, and log collection in nginx is the same. Reference kafka elasticsearch kibana filebeat go-stash filebeat Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"trace.html":{"url":"trace.html","title":"Trace","keywords":"","body":"Trace [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Foreword In the microservice architecture, the call chain may be very long, from http to rpc, and from rpc to http. Developers want to know the call status and performance of each link, the best solution is full link tracking. The tracking method is to generate its own spanID at the beginning of a request, and pass it down along the entire request link. We use this spanID to view the status of the entire link and performance issues. Let's take a look at the link implementation of go-zero. Code structure spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 Concept SpanContext Before introducing span, first introduce context. SpanContext saves the context information of distributed tracing, including Trace id, Span id and other content that needs to be passed downstream. The implementation of OpenTracing needs to pass the SpanContext through a certain protocol to associate the Span in different processes to the same Trace. For HTTP requests, SpanContext is generally passed using HTTP headers. Below is the spanContext implemented by go-zero by default type spanContext struct { traceId string // TraceID represents the globally unique ID of tracer spanId string // SpanId indicates the unique ID of a span in a single trace, which is unique in the trace } At the same time, developers can also implement the interface methods provided by SpanContext to realize their own contextual information transfer: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // Custom operation TraceId, SpanId } Span A REST call or database operation, etc., can be used as a span. span is the smallest tracking unit of distributed tracing. A trace is composed of multiple spans. The tracking information includes the following information: type Span struct { ctx spanContext serviceName string operationName string startTime time.Time flag string children int } Judging from the definition structure of span: In microservices, this is a complete sub-calling process, with the start of the call startTime, the context structure spanContext that marks its own unique attribute, and the number of child nodes of fork. Example application In go-zero, http and rpc have been integrated as built-in middleware. We use http, rpc, take a look at how tracing is used: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } Set header -> carrier to get the traceId and other information in the header Open a new span and encapsulate \"traceId, spanId\" in the context Obtain traceId and spanId from the aforementioned carrier \"that is, header\" -See if it is set in the header -If it is not set, it will be randomly generated and returned Generate a new ctx from request, encapsulate the corresponding information in ctx, and return From the above context, copy a copy to the current request In this way, the information of the span is passed to the downstream service along with the request. RPC There are client, server in rpc, so from tracing there are also clientTracing, serverTracing. The logic of serveTracing is basically the same as that of http. Let’s take a look at how clientTracing is used? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** Add the data in the pair to ctx in the form of a map ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } Get the span context information brought down by the upstream Create a new ctx from the acquired span, span \"inherit the traceId of the parent span\" Add the span generated data to ctx, pass it to the next middleware, and flow downstream Summary go-zero obtains the link traceID by intercepting the request, and then assigns a root Span at the entry of the middleware function, and then splits the child Spans in subsequent operations. Each span has its own specific identification. After Finsh Will be collected in the link tracking system. Developers can trace the traceID through the ELK tool to see the entire call chain. At the same time, go-zero does not provide a complete set of trace link solutions. Developers can encapsulate the existing span structure of go-zero, build their own reporting system, and access links such as jaeger, zipkin, etc. Tracking tool. Reference go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-monitor.html":{"url":"service-monitor.html","title":"Monitor","keywords":"","body":"Monitor [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In microservice governance, service monitoring is also a very important link. Monitoring whether a service is working normally needs to be carried out from multiple dimensions, such as:* mysql indicators mongo indicators redis indicator Request log Service index statistics Service health check ... The monitoring work is very large, and this section only uses the service indicator monitoring as an example for illustration. Microservice indicator monitoring based on prometheus After the service is online, we often need to monitor the service so that we can find the problem early and make targeted optimization. The monitoring can be divided into various forms, such as log monitoring, call chain monitoring, indicator monitoring, and so on. Through indicator monitoring, the changing trend of service indicators can be clearly observed, and the operating status of the service can be understood, which plays a very important role in ensuring the stability of the service. Prometheus is an open source system monitoring and warning tool that supports a powerful query language, PromQL, allowing users to select and aggregate time series data in real time. Time series data is actively pulled by the server through the HTTP protocol, or it can be pushed through an intermediate gateway. Data, you can obtain monitoring targets through static configuration files or service discovery Prometheus architecture The overall architecture and ecosystem components of Prometheus are shown in the following figure: Prometheus Server pulls monitoring indicators directly from the monitoring target or indirectly through the push gateway. It stores all captured sample data locally and executes a series of rules on this data to summarize and record new time series or existing data. Generate an alert. The monitoring data can be visualized through Grafana or other tools go-zero service indicator monitoring based on prometheus The go-zero framework integrates prometheus-based service indicator monitoring. Below we use go-zero’s official example short url to demonstrate how to collect and monitor service indicators: The first step is to install Prometheus first, please refer to the official documentation for the installation steps go-zero does not enable prometheus monitoring by default. The opening method is very simple. You only need to add the following configuration to the shorturl-api.yaml file, where Host is the Prometheus Server address, which is a required configuration, the Port port is not filled in and the default is 9091, and the Path is used The path to pull metrics is /metrics by default Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics Edit the prometheus configuration file prometheus.yml, add the following configuration, and create targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json Edit the targets.json file, where targets is the target address configured by shorturl, and add several default tags [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] Start the prometheus service, listening on port 9090 by default $ prometheus --config.file=prometheus.yml Enter http://127.0.0.1:9090/ in the browser, and then click Status -> Targets to see the job whose status is Up, and the default label we configured can be seen in the Labels column Through the above steps, we have completed the configuration work of Prometheus for the indicator monitoring collection of the shorturl service. For the sake of simplicity, we have performed manual configuration. In the actual production environment, we generally use the method of regularly updating configuration files or service discovery to configure monitoring. Goals, space is limited, not explained here, interested students please check the relevant documents on their own Types of indicators monitored by go-zero go-zero currently adds monitoring of request metrics to the http middleware and rpc interceptor. Mainly from the two dimensions of request time and request error. The request time uses the Histogram metric type to define multiple Buckets to facilitate quantile statistics. The request error uses the Counter type, and the path tag rpc metric is added to the http metric. Added the method tag for detailed monitoring. Next, demonstrate how to view monitoring indicators: First execute the following command multiple times on the command line $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" Open Prometheus and switch to the Graph interface, and enter the {path=\"/shorten\"} command in the input box to view the monitoring indicators, as shown below: We use PromQL grammar query to filter the indicators whose path is /shorten, and the results show the indicator name and indicator value. The code value in the http_server_requests_code_total indicator is the status code of http, 200 indicates that the request is successful, and http_server_requests_duration_ms_bucket separately counts the results of different buckets. , You can also see that all the indicators have added the default indicators we configured The Console interface mainly displays the index results of the query. The Graph interface provides us with a simple graphical display interface. In the actual production environment, we generally use Grafana for graphical display. grafana dashboard Grafana is a visualization tool with powerful functions and supports multiple data sources such as Prometheus, Elasticsearch, Graphite, etc. For simple installation, please refer to the official documentation. The default port of grafana is 3000. After installation, enter http://localhost:3000/ in the browser. , The default account and password are both admin. The following demonstrates how to draw the visual interface based on the above indicators: Click on the left sidebar Configuration->Data Source->Add data source to add a data source, where the HTTP URL is the address of the data source Click on the left sidebar to add dashboard, and then add Variables to facilitate filtering for different tags, such as adding app variables to filter different services Enter the dashboard and click Add panel in the upper right corner to add a panel to count the qps of the interface in the path dimension The final effect is shown below. Different services can be filtered by service name. The panel shows the trend of qps with path /shorten. Summary The above demonstrates the simple process of go-zero based on prometheus+grafana service indicator monitoring. In the production environment, different dimensions of monitoring and analysis can be done according to the actual scenario. Now go-zero's monitoring indicators are mainly for http and rpc, which is obviously insufficient for the overall monitoring of the service, such as the monitoring of container resources, the monitoring of dependent mysql, redis and other resources, and the monitoring of custom indicators, etc. Go-zero will continue to optimize in this regard. Hope this article can help you Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl is a code generation tool under the go-zero microservice framework. Using goctl can significantly improve development efficiency and allow developers to focus their time on business development. Its functions include: api service generation rpc service generation model code generation template management This section will contain the following: Auto Completion Commands & Flags API Commands RPC Commands Model Commands Plugin Commands More Commands goctl? Many people will pronounce goctl as go-C-T-L. This is a wrong way of thinking. You should refer to go control and pronounce ɡō kənˈtrōl. View version information $ goctl -v If goctl is installed, it will output text information in the following format: goctl version ${version} ${os}/${arch} For example output: goctl version 1.1.5 darwin/amd64 Version number description version: goctl version number os: Current operating system name arch: Current system architecture name Install goctl The way one(go get) $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl Use this command to install the goctl tool into the GOPATHbin directory The way two (fork and build) Pull a source code from the go-zero code repository git@github.com:zeromicro/go-zero.git, enter the tools/goctl directory to compile the goctl file, and then add it to the environment variable. After the installation is complete, execute goctl -v. If the version information is output, the installation is successful, for example: $ goctl -v goctl version 1.1.4 darwin/amd64 FAQ command not found: goctl Please make sure that goctl has been installed, or whether goctl has been correctly added to the environment variables of the current shell. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"Auto Completion","keywords":"","body":"goctl autocomplete goctl autocomplete only supports unix-like operating systems Usage $ goctl completion -h Name. goctl autocomplete - generates completion scripts, it only works on unix-like operating systems Usage. goctl completion [command options] [arguments...] (command options). Options. --name value, -n value filename of the autocomplete script, default is [goctl_autocomplete] Generate autocomplete file $ goctl completion Generate autocomplete successfully! Execute the following script to set up the shell. echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell configuration zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc Demo effect Use the tab key to bring up the autocomplete prompt $ goctl api -- generate api-related files bug -- report a bug completion -- generates a completion script, which is only available for unix-like operating systems docker -- generates a Docker file help h -- displays a list of commands or help information for a command kube -- generates kubernetes files migrate -- migrate from tal-tech to zeromicro model -- Generate model code rpc -- generate rpc code template -- Template operations upgrade -- upgrade goctl to the latest version Translated with www.DeepL.com/Translator (free version) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"Commands & Flags","keywords":"","body":"goctl command list [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl bug (report a bug) upgrade (upgrade goctl to latest version) env (check or edit goctl environment) --write, -w: edit goctl environment check (detect goctl env and dependency tools) --force, -f: silent installation of non-existent dependencies --install, -i: install dependencies if not found migrate (migrate from tal-tech to zeromicro) --verbose, -v: verbose enables extra logging --version: the target release version of github.com/zeromicro/go-zero to migrate api (generate api related files) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure -o: the output api file new (fast create api service) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (format api files) --declare: use to skip check api types already declare --dir: the format target dir --iu: ignore update --stdin: use stdin to input api doc content, press \"ctrl + d\" to send EOF validate (validate api file) --api: validate target api file doc (generate doc files) --dir: the target dir -o: the output markdown directory go (generate go files for provided api in yaml file) --api: the api file --branch: the branch of the remote repo, it does work with --remote --dir: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (generate java files for provided api in api file) --api: the api file --dir: the target dir ts (generate ts files for provided api in api file) --api: the api file --caller: the web api caller --dir: the target dir --unwrap: unwrap the webapi caller for import --webapi: the web api file path dart (generate dart files for provided api in api file) --api: the api file --dir: the target dir --hostname: hostname of the server --legacy: legacy generator for flutter v1 kt (generate kotlin code for provided api file) --api: the api file --dir: the target directory --pkg: define package name for kotlin file plugin (custom file generator) --api: the api file --dir: the target directory --plugin, -p: the plugin file --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (generate Dockerfile) --branch: the branch of the remote repo, it does work with --remote --go: the file that contains main function --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --port: the port to expose, default none (default: 0) --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --scratch: use scratch for the base docker image --tz: the timezone of the container (default: Asia/Shanghai) --version: the goctl builder golang image version kube (generate kubernetes files) deploy (generate deployment yaml file) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --image: the docker image of deployment --limitCpu: the limit cpu to deploy (default: 1000) --limitMem: the limit memory to deploy (default: 1024) --maxReplicas: the max replicas of deploy (default: 10) --minReplicas: the min replicas to deploy (default: 3) --name: the name of deployment --namespace: the namespace of deployment --nodePort: the nodePort of the deployment to expose (default: 0) --port: the port of the deployment to listen on pod (default: 0) --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --replicas: the number of replicas to deploy (default: 3) --requestCpu: the request cpu to deploy (default: 500) --requestMem: the request memory to deploy (default: 512) --revisions: the number of revision history to limit (default: 5) --secret: the secret to image pull from registry --serviceAccount: the ServiceAccount for the deployment -o: the output yaml file rpc (generate rpc code) new (generate rpc demo service) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: whether the command execution environment is from idea plugin. [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (generate proto template) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --out, -o: the target path of proto --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure protoc (generate grpc code) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out: the zrpc output directory proto (generate rpc from proto) --branch: the branch of the remote repo, it does work with --remote --dir, -d: the target path of the code --go_opt: native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional] --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: whether the command execution environment is from idea plugin. [optional] --proto_path, -I: native command of protoc, specify the directory in which to search for imports. [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --src, -s: the file path of the proto source file --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] model (generate model code) mysql (generate mysql model) ddl (generate mysql model from ddl) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --database, --db: the name of database [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --src, -s: the path or path globbing patterns of the ddl --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (generate model from datasource) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: the table or table globbing patterns in the database --url: the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" pg (generate postgresql model) datasource (generate model from datasource) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --schema, -s: the table schema, default is [public] --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: the table or table globbing patterns in the database --url: the data source of database,like \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (generate mongo model) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t: specified model type name template (template operation) init (initialize the all templates(force update)) --home: the goctl home path of the template clean (clean the all cache templates) --home: the goctl home path of the template update (update template of the target category to the latest) --category, -c: the category of template, enum [api,rpc,model,docker,kube] --home: the goctl home path of the template revert (revert the target template to the latest) --category, -c: the category of template, enum [api,rpc,model,docker,kube] --home: the goctl home path of the template --name, -n: the target file name of template completion (generation completion script, it only works for unix-like OS) --name, -n: the filename of auto complete script, default is [goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-api.html":{"url":"goctl-api.html","title":"API Commands","keywords":"","body":"API Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl api is one of the core modules in goctl. It can quickly generate an api service through the .api file with one click. If you just start a go-zero api demo project, You can complete an api service development and normal operation without even coding. In traditional api projects, we have to create directories at all levels, write structures, Define routing, add logic files, this series of operations, if calculated according to the business requirements of a protocol, it takes about 5 to 6 minutes for the entire coding to actually enter the writing of business logic. This does not consider the various errors that may occur during the writing process. With the increase of services and the increase of agreements, the time for this part of the preparation work will increase proportionally. The goctl api can completely replace you to do this part of the work, no matter how many agreements you have, in the end, it only takes less than 10 seconds to complete. [!TIP] The structure is written, and the route definition is replaced by api, so in general, it saves you the time of creating folders, adding various files and resource dependencies. API command description $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help As you can see from the above, according to the different functions, the api contains a lot of self-commands and flags, let’s focus on it here The go subcommand, which function is to generate golang api services, let's take a look at the usage help through goctl api go -h: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir: Code output directory --api: Specify the api source file --style: Specify the file name style of the generated code file, see for details https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md Usage example $ goctl api go -api user.api -dir . -style gozero Guess you wants API IDL API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"RPC Commands","keywords":"","body":"RPC Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Goctl Rpc is an rpc service code generation module under goctl scaffolding, supporting proto template generation and rpc service code generation, through this tool to generate code you only need to focus on business logic writing instead of writing some repetitive code. This allows us to focus on the business, thus speeding up the development efficiency and reducing the error rate of the code. Features Easy to use Fast and efficient development Low error rate Close to protoc Quick start Way 1: Quickly generate greet services Generated by the command goctl rpc new ${servieName} If you generate the greet rpc service. $ goctl rpc new greet The code structure after execution is as follows: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] See rpc directory for details of the new version of the directory. Way 2: Generate rpc service by specifying proto 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\". /user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } Generate rpc service code $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. Preparing Installed the go environment protoc & protoc-gen-go are installed, and environment variables have been set For more questions, see Notes Usage rpc service generation usage goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=. /pb --go-grpc_out=. /pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority, the git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote Parameters description --zrpc_out optional, default is the directory where the proto file is located, the target directory of the generated code --style optional, the naming style of the output directory, see https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md for details --home optional, specify the template path --remote Optional, specify the template remote repository. --branch Optional, specify the template remote repository branch, use with --remote You can understand that zrpc code generation is done with goctl rpc $protoc_command --zrpc_out=${output} templates, as in the original command to generate grpc code $ protoc user.proto --go_out=. --go-grpc_out=. then the zrpc code command would be $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out and --go-grpc_out must generate the same final directory The final directories generated by --go_out & --go-grpc_out and --zrpc_out must not be the same directory, otherwise pb.go and _grpc.pb.go are at the same level as the main function, which is not allowed. The directories produced by --go_out and --go-grpc_out are affected by the go_package values in the --go_opt and --grpc-go_opt and proto source files. To understand the generation logic here, it is recommended to read the Official documentation: Go Generated Code What developers need to do Focus on business code writing, leave the repetitive, non-business related work to goctl, after generating the rpc service code, developers only need to modify write configuration files in the service (etc/xx.json, internal/config/config.go) Business logic writing in the service (internal/logic/xxlogic.go) Writing of resource contexts in services (internal/svc/servicecontext.go) Caution proto does not support simultaneous generation of multiple files at the moment proto does not support external dependency package introduction, message does not support inline Currently main file, shared file, handler file will be forced to overwrite, and developers need to write manually will not overwrite the generation, this kind of code in the header are // Code generated by goctl. do not EDIT! // Source: xxx.proto Please be careful not to write business code in it; and do not write it inside business code. proto import For requestType and returnType in rpc must be defined in main proto file, for message in proto you can import other proto files like protoc. proto example: errorimport syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request and return do not support import } Correct import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// support import } message Response { base.Out out = 2;// support import } service Greet { rpc Ping(Request) returns(Response); } Guess what you want to see rpc directory rpc-config rpc-call Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-model.html":{"url":"goctl-model.html","title":"Model Commands","keywords":"","body":"Model Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl model is one of the components in the tool module under go-zero. It currently supports the recognition of mysql ddl for model layer code generation. It can be selectively generated with or without redis cache through the command line or idea plug-in (supported soon) The code logic. Quick start Generated by ddl $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c CURD code can be quickly generated after executing the above command. model ├── usermodel.go ├── usermodel_gen.go └── vars.go Generated by datasource $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } Usage $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help Generation rules Default rule By default, users will create createTime and updateTime fields (ignoring case and underscore naming style) when creating a table, and the default values are both CURRENT_TIMESTAMP, and updateTime supports ON UPDATE CURRENT_TIMESTAMP. For these two fields, insert, It will be removed when update is not in the assignment scope. Of course, if you don't need these two fields, it does not matter. ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote Generate code only basic CURD structure. Cache For the cache, I chose to list it in the form of question and answer. I think this can more clearly describe the function of the cache in the model. What information will the cache? For the primary key field cache, the entire structure information will be cached, while for the single index field (except full-text index), the primary key field value will be cached. Does the data update (update) operation clear the cache? Yes, but only clear the information in the primary key cache, why? I won't go into details here. Why not generate updateByXxx and deleteByXxx codes based on single index fields? There is no problem in theory, but we believe that the data operations of the model layer are based on the entire structure, including queries. I do not recommend querying only certain fields (no objection), otherwise our cache will be meaningless. Why not support the code generation layer of findPageLimit and findAll? At present, I think that in addition to the basic CURD, the other codes are all business-type codes. I think it is better for developers to write according to business needs. Type conversion rules mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"Plugin Commands","keywords":"","body":"Plugin Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Goctl supports custom plugins for api, so how do I customize a plugin? Let's take a look at an example of how to finally use it below. $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . The above command can be broken down into the following steps: goctl parsing api file goctl passes the parsed structure ApiSpec and parameters to the goctl-android executable file goctl-android customizes the generation logic according to the ApiSpec structure. The first part of this command goctl api plugin -p is a fixed parameter, goctl-android=\"android -package com.tal\" is a plugin parameter, where goctl-android is the plugin binary file, and android -package com.tal is a custom parameter of the plugin , -Api user.api -dir. Is a common custom parameter for goctl. How to write a custom plug-in? A very simple custom plug-in demo is included in the go-zero framework. The code is as follows: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() The function of this line of code is to parse the data passed from goctl, which contains the following parts: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api: defines the structure data of the api file Style: optional, it is used to control file naming conventions Dir: workDir Complete android plugin demo project based on plugin https://github.com/zeromicro/goctl-android Guess you wants API Directory Structure API IDL API Configuration API Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-other.html":{"url":"goctl-other.html","title":"More Commands","keywords":"","body":"More Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl docker goctl kube goctl docker goctl docker can quickly generate a Dockerfile to help developers/operations and maintenance personnel speed up the deployment pace and reduce deployment complexity. Prepare docker install Dockerfile note Choose the simplest mirror: For example, alpine, the entire mirror is about 5M Set mirror time zoneRUN apk add --no-cache tzdata ENV TZ Asia/Shanghai Multi-stage build Otherwise, an executable file will be built in the first stage of construction to ensure that the build process is independent of the host The second stage uses the output of the first stage as input to construct the final minimalist image Dockerfile writing process First install the goctl tool $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl Create a hello service under the greet project $ goctl api new hello The file structure is as follows: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go Generate a Dockerfile in the hello directory$ goctl docker -go hello.go Dockerfile: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] To build mirror in the greet directory $ docker build -t hello:v1 -f service/hello/Dockerfile . View mirror hello v1 5455f2eaea6b 7 minutes ago 18.1MB It can be seen that the mirror size is about 18M. Start service$ docker run --rm -it -p 8888:8888 hello:v1 Test service$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker summary The goctl tool greatly simplifies the writing of Dockerfile files, provides best practices out of the box, and supports template customization. goctl kube goctl kube provides the function of quickly generating a k8s deployment file, which can speed up the deployment progress of developers/operations and maintenance personnel and reduce deployment complexity. Have a trouble to write K8S deployment files? K8S yaml has a lot of parameters, need to write and check? How to set the number of retained rollback versions? How to detect startup success, how to detect live? How to allocate and limit resources? How to set the time zone? Otherwise, the print log is GMT standard time How to expose services for other services to call? How to configure horizontal scaling based on CPU and memory usage? First, you need to know that you have these knowledge points, and secondly, it is not easy to understand all these knowledge points, and again, it is still easy to make mistakes every time you write! Create service image For demonstration, here we take the redis:6-alpine image as an example. 完整 K8S Deployment file writing process First install the goctl tool $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl One-click generation of K8S deployment files $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 The generated yaml file is as follows: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 Deploy the service, if the adhoc namespace does not exist, please create it through kubectl create namespace adhoc $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created View service permission status $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s Test service$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube summary The goctl tool greatly simplifies the writing of K8S yaml files, provides best practices out of the box, and supports template customization. Guess you wants Prepare API Directory Structure API IDL API Configuration API Commands Docker K8s Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-manage.html":{"url":"template-manage.html","title":"Template","keywords":"","body":"Template Command Custom Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-cmd.html":{"url":"template-cmd.html","title":"Command","keywords":"","body":"Template Operation Template is the basis of data-driven generation, all code (rest api, rpc, model, docker, kube) generation will rely on template. By default, the template generator selects the in-memory template for generation, while for developers who need to modify the template, they need to drop the template and make template changes in the next code generation. For developers who need to modify the templates, they need to modify the templates, and then the next time the code is generated, it will load the templates under the specified path to generate. Help NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help Init NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template Clean NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template Update NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template Revert NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home Specify the template storage path Template loading You can specify the folder where the template is located by --home during code generation, and the commands that have been supported to specify the template directory are goctl api go Details can be found in goctl api go --help for help goctl docker Details can be viewed with goctl docker --help goctl kube Details can be viewed with goctl kube --help goctl rpc new Details can be viewed with goctl rpc new --help goctl rpc proto Details can be viewed with goctl rpc proto --help goctl model mysql ddl Details can be viewed with goctl model mysql ddl --help goctl model mysql datasource Details can be viewed with goctl model mysql datasource --help goctl model postgresql datasource Details can be viewed with goctl model mysql datasource --help goctl model mongo Details can be viewed with goctl model mongo --help The default (when --home is not specified) is to read from the $HOME/.goctl directory. Example Initialize the template to the specified $HOME/template directory$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! Greet rpc generation using $HOME/template template$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template.html":{"url":"template.html","title":"Custom","keywords":"","body":"Template Modification Scenario Implement a uniformly formatted body accordingly, in the following format. { \"code\": 0, \"msg\": \"OK\", \"data\": {}// ① } ① 实际相应数据 [!TIP] The code generated by go-zero does not process it Preparation We go ahead and write a Response method in the response package under the project with module as greet, with a directory tree similar to the following. greet ├── response │ └── response.go └── xxx... The code is as follows package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } Modify the handler template $ vim ~/.goctl/api/handler.tpl Replace the template with the following package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① Replace with your real response package name, for reference only ② Customized template content [!TIP] 1.If there is no local ~/.goctl/api/handler.tpl file, you can initialize it with the template initialization command goctl template init Comparison Before func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // The following content will be replaced by custom templates if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } After func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } Comparison of response body Before { \"message\": \"Hello go-zero!\" } After { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } Summary This document only describes the process of customizing the template for the corresponding example of http, in addition to the following scenarios of customizing the template. model layer adds kmq model layer to generate the model instance of the option to be valid http customize the corresponding format ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"extended-reading.html":{"url":"extended-reading.html","title":"Extended","keywords":"","body":"Components [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The component center will include all components in the go-zero core folder, Therefore, it will be relatively large, and this resource will continue to be updated, and everyone is welcome to contribute to the document. This section will contain the following directories (in order of document update time): shorturl logx bloom executors fx mysql redis-lock periodlimit tokenlimit TimingWheel Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"logx.html":{"url":"logx.html","title":"logx","keywords":"","body":"logx [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Example var c logx.LogConf // Initialize the configuration from the yaml file conf.MustLoad(\"config.yaml\", &c) // logx is initialized according to the configuration logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() Initialization logx has many configurable items, you can refer to the definition in logx.LogConf. Currently available logx.MustSetUp(c) Perform the initial configuration. If the initial configuration is not performed, all the configurations will use the default configuration. Level The print log levels supported by logx are: info error server fatal slow stat You can use the corresponding method to print out the log of the corresponding level. At the same time, in order to facilitate debugging and online use, the log printing level can be dynamically adjusted. The level can be set through logx.SetLevel(uint32) or through configuration initialization. The currently supported parameters are: const ( // Print all levels of logs InfoLevel = iotas // Print errors, slows, stacks logs ErrorLevel // Only print server level logs SevereLevel ) Log mode At present, the log printing mode is mainly divided into two types, one is file output, and the other is console output. The recommended way, when using k8s, docker and other deployment methods, you can output the log to the console, use the log collector to collect and import it to es for log analysis. If it is a direct deployment method, the file output method can be used, and logx will automatically create log files corresponding to 5 corresponding levels in the specified file directory to save the logs. . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log At the same time, the file will be divided according to the natural day. When the specified number of days is exceeded, the log file will be automatically deleted, packaged and other operations. Disable log If you don't need log printing, you can use logx.Close() to close the log output. Note that when log output is disabled, it cannot be opened again. For details, please refer to the implementation of logx.RotateLogger and logx.DailyRotateRule. Close log Because logx uses asynchronous log output, if the log is not closed normally, some logs may be lost. The log output must be turned off where the program exits: logx.Close() Log configuration and shutdown related operations have already been done in most places such as rest and zrpc in the framework, so users don't need to care. At the same time, note that when the log output is turned off, the log cannot be printed again. Recommended writing: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration When we print the log, we may need to print the time-consuming situation, we can use logx.WithDuration(time.Duration), refer to the following example: startTime := timex.Now() // Database query rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } Will output the following format: {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} In this way, it is easy to collect statistics about slow sql related information. TraceLog tracingEntry is customized for link tracing log output. You can print the traceId and spanId information in the context. With our rest and zrpc, it is easy to complete the related printing of the link log. The example is as follows: logx.WithContext(context.Context).Info(\"This is info!\") SysLog Some applications may use system log for log printing. Logx uses the same encapsulation method, which makes it easy to collect log-related logs into logx. logx.CollectSysLog() Log configuration related LogConf Define the basic configuration required for the logging system The complete definition is as follows: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode defines the log printing method. The default mode is console, which will print to the console. The currently supported modes are as follows: console Print to the console file Print to access.log, error.log, stat.log and other files in the specified path volume In order to print to the storage that the mount comes in in k8s, because multiple pods may overwrite the same file, the volume mode automatically recognizes the pod and writes separate log files according to the pod. Path Path defines the output path of the file log, the default value is logs. Level Level defines the log printing level, and the default value is info. The currently supported levels are as follows: info error severe Compress Compress defines whether the log needs to be compressed, the default value is false. When Mode is file mode, the file will finally be packaged and compressed into a .gz file. KeepDays KeepDays defines the maximum number of days to keep logs. The default value is 0, which means that old logs will not be deleted. When Mode is file mode, if the maximum retention days are exceeded, the old log files will be deleted. StackCooldownMillis StackCooldownMillis defines the log output interval, the default is 100 milliseconds. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"bloom.html":{"url":"bloom.html","title":"bloom","keywords":"","body":"bloom [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The go-zero microservice framework provides many out-of-the-box tools. Good tools can not only improve the performance of the service, but also improve the robustness of the code to avoid errors, and realize the uniformity of the code style for others to read, etc. A series of articles will respectively introduce the use of tools in the go-zero framework and their implementation principles. Bloom filter bloom When doing server development, I believe you have heard of Bloom filters, you can judge whether a certain element is in the collection, because there are certain misjudgments and delete complex problems, the general usage scenario is: to prevent cache breakdown (to prevent malicious Attacks), spam filtering, cache digests, model detectors, etc., to determine whether there is a row of data to reduce disk access and improve service access performance. The simple cache package bloom.bloom provided by go-zero, the simple way to use it is as follows. // Initialize redisBitSet store := redis.NewRedis(\"redis 地址\", redis.NodeType) // Declare a bitSet, key=\"test_key\" name and bits are 1024 bits bitSet := newRedisBitSet(store, \"test_key\", 1024) // Determine whether the 0th bit exists isSetBefore, err := bitSet.check([]uint{0}) // Set the 512th bit to 1 err = bitSet.set([]uint{512}) // Expires in 3600 seconds err = bitSet.expire(3600) // Delete the bitSet err = bitSet.del() Bloom briefly introduced the use of the most basic redis bitset. The following is the real bloom implementation. Position the element hash // The element is hashed 14 times (const maps=14), and byte (0-13) is appended to the element each time, and then the hash is performed. // Take the modulo of locations[0-13], and finally return to locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i Add elements to bloom // We can find that the add method uses the set methods of getLocations and bitSet. // We hash the elements into uint slices of length 14, and then perform the set operation and store them in the bitSet of redis. func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } Check if there is an element in bloom // We can find that the Exists method uses the check method of getLocations and bitSet // We hash the elements into uint slices of length 14, and then perform bitSet check verification, return true if it exists, false if it does not exist or if the check fails func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } This section mainly introduces the core.bloom tool in the go-zero framework, which is very practical in actual projects. Good use of tools is very helpful to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In go-zero, executors act as a task pool, do multi-task buffering, and use tasks for batch processing. Such as: clickhouse large batch insert, sql batch insert. At the same time, you can also see executors in go-queue [In queue, ChunkExecutor is used to limit the byte size of task submission]. So when you have the following requirements, you can use this component: Submit tasks in batches Buffer part of tasks and submit lazily Delay task submission Before explaining it in detail, let's give a rough overview: Interface design Under the executors package, there are the following executors: Name Margin value bulkexecutor Reach maxTasks [Maximum number of tasks] Submit chunkexecutor Reach maxChunkSize[Maximum number of bytes] Submit periodicalexecutor basic executor delayexecutor Delay the execution of the passed fn() lessexecutor You will see that except for the special functions of delay and less, the other three are all combinations of executor + container: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // Option mode: It appears in many places in go-zero. In multiple configurations, better design ideas // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute the function that actually does the execution] [maxTasks execution critical point] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. It can be seen that the underlying bulkexecutor depends on the periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } And this container is an interface: TaskContainer interface { // Add task to container AddTask(task interface{}) bool // Is actually to execute the incoming execute func() Execute(tasks interface{}) // When the critical value is reached, remove all tasks in the container and pass them to execute func() through the channel for execution RemoveAll() interface{} } This shows the dependency between: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] So if you want to complete your own executor, you can implement these three interfaces of container, and then combine with periodicalexecutor. So back to the picture 👆, our focus is on the periodicalexecutor, and see how it is designed? How to use First look at how to use this component in business: There is a timed service to perform data synchronization from mysql to clickhouse at a fixed time every day: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } Initialize bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() is the real insert execution function [requires developers to write specific business logic by themselves] dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // The container will automatically refresh the task to execute every 3s. executors.WithBulkTasks(10240), // The maximum number of tasks for the container. Generally set to a power of 2 ) } [!TIP] An additional introduction: clickhouse is suitable for mass insertion, because the insert speed is very fast, mass insert can make full use of clickhouse Main business logic preparation: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] You may be wondering why Flush(), Wait() is needed, and I will analyze it through the source code later. There are 3 steps to use as a whole: Add(): Add to task Flush(): Refresh tasks in container Wait(): Wait for the completion of all tasks Source code analysis [!TIP] The main analysis here is periodicalexecutor, because the other two commonly used executors rely on it. Initialization func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander: Pass the channel of tasks container: Temporarily store the task of Add() confirmChan: Block Add(), at the beginning of this time, executeTasks() will let go of blocking ticker: To prevent the blocking of Add(), there will be a chance to execute regularly and release the temporarily stored task in time Add() After initialization, the first step in the business logic is to add task to executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask will pop and return tasks in the container if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } In addAndCheck(), AddTask() is controlling the maximum number of tasks. If it exceeds the number of tasks, RemoveAll() will be executed, and the tasks pop of the temporarily stored container will be passed to the commander, followed by goroutine loop reading , And then execute tasks. backgroundFlush() Start a background coroutine, and constantly refresh the tasks in the container: func (pe *PeriodicalExecutor) backgroundFlush() { // Encapsulate go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // Get []tasks from channel case vals := pe.interval*idleRound { // If maxTask is not reached, Flush() err, and last->now is too long, Flush() will be triggered again // Only when this is reversed will a new backgroundFlush() background coroutine be opened pe.guarded = false // Refresh again to prevent missing pe.Flush() return } } } }) } Overall two processes: commander receives the tasks passed by RemoveAll(), then executes it, and releases the blocking of Add() to continue Add() It’s time for ticker, if the first step is not executed, it will automatically Flush() and execute the task. Wait() In backgroundFlush(), a function is mentioned: enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } By enumerating in this way, you can know why you have to bring dts.insertExecutor.Wait() at the end. Of course, you have to wait for all goroutine tasks to complete. Thinking In looking at the source code, I thought about some other design ideas, do you have similar questions: In the analysis of executors, you will find that there are lock in many places [!TIP] There is a race condition in go test, use locking to avoid this situation After analyzing confirmChan, it was found that this submit only appeared, why is it designed like this? It used to be: wg.Add(1) was written in executeTasks(); now it is: first wg.Add(1), then release confirmChan blocking If the execution of executor func is blocked, Add task is still in progress, because there is no block, it may be executed to Executor.Wait() soon, and this is where wg.Wait() appears in wg.Add () before execution, this will be panic For details, please see the latest version of TestPeriodicalExecutor_WaitFast(), you may wish to run on this version to reproduce. Summary There are still a few more analysis of executors, I leave it to you to look at the source code. In short, the overall design: Follow interface-oriented design Flexible use of concurrent tools such as channel and waitgroup The combination of execution unit + storage unit There are many useful component tools in go-zero. Good use of tools is very helpful to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"fx.html":{"url":"fx.html","title":"fx","keywords":"","body":"fx [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR fx is a complete stream processing component. It is similar to MapReduce, fx also has a concurrent processing function: Parallel(fn, options). But at the same time it is not only concurrent processing. From(chan), Map(fn), Filter(fn), Reduce(fn), etc., read from the data source into a stream, process the stream data, and finally aggregate the stream data. Is it a bit like Java Lambda? If you were a Java developer before, you can understand the basic design when you see this. Overall API Let's get an overview of how fx is constructed as a whole: The marked part is the most important part of the entire fx: From APIs such as From(fn), a data stream Stream is generated A collection of APIs for converting, aggregating, and evaluating Stream So list the currently supported Stream API: API Function Distinct(fn) Select a specific item type in fn and de-duplicate it Filter(fn, option) fn specifies specific rules, and the element that meets the rules is passed to the next stream Group(fn) According to fn, the elements in stream are divided into different groups Head(num) Take out the first num elements in stream and generate a new stream Map(fn, option) Convert each ele to another corresponding ele and pass it to the next stream Merge() Combine all ele into one slice and generate a new stream Reverse() Reverse the element in stream. [Use double pointer] Sort(fn) Sort elements in stream according to fn Tail(num) Take out the last num elements of stream to generate a new stream. [Using a doubly linked list] Walk(fn, option) Apply fn to every element of source. Generate a new stream No longer generates a new stream, do the final evaluation operation: API Function ForAll(fn) Process stream according to fn, and no longer generate stream [evaluation operation] ForEach(fn) Perform fn [evaluation operation] on all elements in stream Parallel(fn, option) Concurrently apply the given fn and the given number of workers to each element[evaluation operation] Reduce(fn) Directly process stream [evaluation operation] Done() Do nothing, wait for all operations to complete How to use? result := make(map[string]string) fx.From(func(source chan From() generates stream from a slice Walk() receives and a stream, transforms and reorganizes each ele in the stream to generate a new stream Finally, the stream output (fmt.Println), storage (map,slice), and persistence (db operation) are performed by the evaluation operation Briefly analyze The function naming in fx is semantically. Developers only need to know what kind of conversion is required for the business logic and call the matching function. So here is a brief analysis of a few more typical functions. Walk() Walk() is implemented as the bottom layer by multiple functions throughout fx, such as Map(), Filter(), etc. So the essence is: Walk() is responsible for concurrently applying the passed function to each ele of the input stream and generating a new stream. Following the source code, it is divided into two sub-functions: custom count by worker, default count is worker // Custom workers func (p Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream { pipe := make(chan interface{}, option.workers) go func() { var wg sync.WaitGroup // channel Use buffered channel as a concurrent queue to limit the number of concurrent waitgroup to ensure the completeness of the task completion Another walkUnlimited(): also uses waitgroup for concurrency control, because there is no custom concurrency limit, so there is no other channel for concurrency control. Tail() The introduction of this is mainly because the ring is a doubly linked list, and the simple algorithm is still very interesting. func (p Stream) Tail(n int64) Stream { source := make(chan interface{}) go func() { ring := collection.NewRing(int(n)) // Sequence insertion, the order of the source is consistent with the order of the ring for item := range p.source { ring.Add(item) } // Take out all the items in the ring for _, item := range ring.Take() { source As for why Tail() can take out the last n of the source, this is left for everyone to fine-tune. Here is my understanding: [!TIP] Suppose there is the following scenario,Tail(5) stream size :7 ring size:5 Here you can use the method of pulling apart the ring-shaped linked list, Loop-to-line,At this point, divide the symmetry axis by the full length, flip the extra elements, and the following elements are the parts needed by Tail(5). [!TIP] The graph is used here for a clearer performance, but everyone should also look at the code. Algorithm to be tested Stream Transform Design Analyzing the entire fx, you will find that the overall design follows a design template: func (p Stream) Transform(fn func(item interface{}) interface{}) Stream { // make channel source := make(chan interface{}) // goroutine worker go func() { // transform for item := range p.source { ... source stream return Range(source) } channel as a container for streams Open goroutine to convert source, aggregate, and send to channel Processed,close(outputStream) channel -> stream Summary This concludes the basic introduction of fx. If you are interested in other API source code, you can follow the API list above to read one by one. At the same time, it is also recommended that you take a look at the API of java stream, and you can have a deeper understanding of this stream call. At the same time, there are many useful component tools in go-zero. Good use of tools will greatly help improve service performance and development efficiency. I hope this article can bring you some gains. Reference go-zero Java Stream Stream API in Java 8 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mysql.html":{"url":"mysql.html","title":"mysql","keywords":"","body":"Mysql [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR go-zero provides easier operation of mysql API. [!TIP] But stores/mysql positioning is not an orm framework. If you need to generate model layer code through sql/scheme -> model/struct reverse engineering, developers can use goctl model, this is an excellent feature. Features Provides a more developer-friendly API compared to native Complete the automatic assignment of queryField -> struct Insert \"bulkinserter\" in batches Comes with fuse API has been continuously tested by several services Provide partial assignment feature, do not force strict assignment of struct Connection Let's use an example to briefly explain how to create a mysql connected model: // 1. Quickly connect to a mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. Call in the `servicecontext`, understand the logic layer call of the model upper layer model.NewMysqlModel(heraMysql, tablename), // 3. model layer mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. Create a batch insert [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD Prepare an User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` UserName string `db:\"user_name\"` Sex int `db:\"sex\"` MobilePhone string `db:\"mobile_phone\"` } Among them, userBuilderQueryRows: go-zero provides struct -> [field...] conversion. Developers can use this as a template directly. insert // An actual insert model layer operation func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } Splicing insertsql Pass in insertsql and the struct field corresponding to the placeholder -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...): args... needs to correspond to the placeholder in sql. Otherwise, there will be problems with assignment exceptions. go-zero unified and abstracted operations involving mysql modification as Exec(). So the insert/update/delete operations are essentially the same. For the remaining two operations, the developer can try the above insert process. query You only need to pass in the querysql and model structure, and you can get the assigned model. No need for developers to manually assign values. func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } Declare model struct, splicing querysql conn.QueryRow(&model, querysql, args...): args... corresponds to the placeholder in querysql. [!WARNING] The first parameter in QueryRow() needs to be passed in Ptr \"The bottom layer needs to be reflected to assign a value to struct\" The above is to query one record, if you need to query multiple records, you can use conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } The difference from QueryRow() is: model needs to be set to Slice, because it is to query multiple rows, and multiple models need to be assigned. But at the same time you need to pay attention to ️: the first parameter needs to be passed in Ptr querypartial In terms of use, it is no different from the above-mentioned QueryRow(), \"this reflects the highly abstract design of go-zero.\" the difference: QueryRow(): len(querysql fields) == len(struct), and one-to-one correspondence QueryRowPartial() :len(querysql fields) numA: Number of database fields; numB: the number of defined struct attributes. If numA , but you just need to unify multiple queries, \"multiple struct is defined to return different purposes, and all of them can use the same querysql\", you can use QueryRowPartial() Transaction To perform a series of operations in a transaction, the general process is as follows: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // Any error returned will roll back the transaction if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // You can also continue to perform insert/update/delete related operations return nil }) As in the above example, developers only need to wrap all operations in transaction in a function func(session sqlx.Session) error {}, if the operation in the transaction returns any error, Transact( ) will automatically roll back the transaction. Distributed transactions go-zero has deeply cooperated with dtm and has natively supported distributed transactions, see distributed-transaction for details Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis-lock","keywords":"","body":"redis lock [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Since it is a lock, the first function that comes to mind is: Anti-repeated clicks, only one request has an effect at a time. Since it is redis, it must be exclusive and also have some common features of locks: High performance No deadlock No lock failure after the node is down In go-zero, redis set key nx can be used to ensure that the write is successful when the key does not exist. px can automatically delete the key after the timeout. \"The worst case is that the key is automatically deleted after the timeout, so that there will be no death. lock\" example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. Optional operation, set the redislock expiration time redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"another user is currently operating, please try again later\") } defer func() { recover() redisLock.Release() }() It is the same as when you use sync.Mutex. Lock and unlock, perform your business operations. Acquire the lock lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } First introduce several redis command options, the following are the added options for the set command: ex seconds : Set the key expiration time, in s px milliseconds : set the key expiration time in milliseconds nx : When the key does not exist, set the value of the key xx : When the key exists, the value of the key will be set The input parameters involved in lua script: args example description KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD Unique ID: random string ARGV[2] 30000 Set the expiration time of the lock Then talk about the code features: The Lua script guarantees atomicity \"Of course, multiple operations are implemented as one operation in Redis, that is, a single command operation\" Use set key value px milliseconds nx value is unique When locking, first determine whether the value of the key is consistent with the previous setting, and modify the expiration time if it is consistent Release lock delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } You only need to pay attention to one point when releasing the lock: Can't release other people's locks, can't release other people's locks, can't release other people's locks Therefore, you need to first get(key) == value「key」, and then go to delete if it is true Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit","keywords":"","body":"periodlimit [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Whether in a single service or in a microservice, the API interface provided by the developer for the front end has an upper limit of access. When the frequency of access or the amount of concurrency exceeds its tolerance, we must consider current limit to ensure the interface. Availability or degraded availability. That is, the interface also needs to be installed with a fuse to prevent the system from being paralyzed due to excessive pressure on the system by unexpected requests. This article will introduce periodlimit. Usage const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero adopts a sliding window counting method to calculate the number of accesses to the same resource within a period of time. If it exceeds the specified limit, access is denied. Of course, if you are accessing different resources within a period of time, the amount of access to each resource does not exceed the limit. In this case, a large number of requests are allowed to come in. In a distributed system, there are multiple microservices to provide services. So when instantaneous traffic accesses the same resource at the same time, how to make the counter count normally in the distributed system? At the same time, when computing resources are accessed, multiple calculations may be involved. How to ensure the atomicity of calculations? go-zero counts resource visits with the help of incrby of redis Use lua script to do the whole window calculation to ensure the atomicity of calculation Let's take a look at several key attributes controlled by lua script: argument mean key[1] Logo for access to resources ARGV[1] limit => the total number of requests, if it exceeds the rate limit. Can be set to QPS ARGV[2] window size => sliding window, use ttl to simulate the effect of sliding -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- If it is the first visit, set the expiration time => TTL = window size -- Because it only limits the number of visits for a period if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current As for the above return code, return it to the caller. The caller decides to request subsequent operations: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit The following picture describes the process of request entry and the subsequent situation when the request triggers limit: Subsequent processing If a large batch of requests comes in at a certain point in the service, the periodlimit reaches the limit threshold in a short period of time, and the set time range is far from reaching. The processing of subsequent requests becomes a problem. It is not processed in periodlimit, but code is returned. The processing of subsequent requests is left to the developer. If it is not processed, it is simply to reject the request If these requests need to be processed, developers can use mq to buffer the requests to ease the pressure of the requests Use tokenlimit to allow temporary traffic impact So in the next article, we will talk about tokenlimit Summary The periodlimit current limiting scheme in go-zero is based on redis counters. By calling redis lua script, it guarantees the atomicity of the counting process and guarantees that the counting is normal under distributed conditions. However, this scheme has disadvantages because it needs to record all behavior records within the time window. If this amount is particularly large, memory consumption will become very serious. Reference go-zero periodlimit Distributed service current limit actual combat, has already lined up the pits for you tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"tokenlimit","keywords":"","body":"tokenlimit [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section will introduce its basic usage through token limit (tokenlimit). Usage const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit On the whole, the token bucket production logic is as follows: The average sending rate configured by the user is r, then a token is added to the bucket every 1/r second; Assume that at most b tokens can be stored in the bucket. If the token bucket is full when the token arrives, then the token will be discarded; When the traffic enters at the rate v, the token is taken from the bucket at the rate v, the traffic that gets the token passes, and the traffic that does not get the token does not pass, and the fuse logic is executed; go-zero adopts the method of lua script under both types of current limiters, relying on redis to achieve distributed current limiting, and lua script can also achieve atomicity of token production and read operations. Let's take a look at several key attributes controlled by lua script: argument mean ARGV[1] rate 「How many tokens are generated per second」 ARGV[2] burst 「Maximum token bucket」 ARGV[3] now_time「Current timestamp」 ARGV[4] get token nums 「The number of tokens that the developer needs to obtain」 KEYS[1] Tokenkey representing the resource KEYS[2] The key that represents the refresh time -- Return whether the expected token can be obtained alive local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:How long does it take to fill the token_bucket local fill_time = capacity/rate -- Round down the fill time local ttl = math.floor(fill_time*2) -- Get the number of remaining tokens in the current token_bucket -- If it is the first time to enter, set the number of token_bucket to the maximum value of the token bucket local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- The time when the token_bucket was last updated local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- Calculate the number of new tokens based on the span between the current time and the last update time, and the rate of token production -- If it exceeds max_burst, excess tokens produced will be discarded local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- Update the new token number and update time redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed It can be seen from the above that the lua script: only involves the operation of the token, ensuring that the token is produced and read reasonably. Function analysis Seen from the above flow: There are multiple guarantee mechanisms to ensure that the current limit will be completed. If the redis limiter fails, at least in the process rate limiter will cover it. Retry the redis limiter mechanism to ensure that it runs as normally as possible. Summary The tokenlimit current limiting scheme in go-zero is suitable for instantaneous traffic shocks, and the actual request scenario is not at a constant rate. The token bucket is quite pre-request, and when the real request arrives, it won't be destroyed instantly. When the traffic hits a certain level, consumption will be carried out at a predetermined rate. However, in the production of token, dynamic adjustment cannot be made according to the current flow situation, and it is not flexible enough, and further optimization can be carried out. In addition, you can refer to Token bucket WIKI which mentioned hierarchical token buckets, which are divided into different queues according to different traffic bandwidths. Reference go-zero tokenlimit Redis Rate Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"TimingWheel","keywords":"","body":"TimingWheel [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This article introduces the delayed operation in go-zero. Delayed operation, two options can be used: Timer: The timer maintains a priority queue, executes it at the time, and then stores the tasks that need to be executed in the map The timingWheel in collection maintains an array for storing task groups, and each slot maintains a doubly linked list of tasks. When the execution starts, the timer executes the tasks in a slot every specified time. Scheme 2 reduces the maintenance task from the priority queue O(nlog(n)) to the doubly linked list O(1), and the execution of the task only needs to poll the tasks O(N) at a time point. Priority queue, put and delete elements O(nlog(n)). timingWheel in cache First, let's first talk about the use of TimingWheel in the cache of collection: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel This is the initialization of cache and the initialization of timingWheel at the same time for key expiration processing. The parameters in turn represent: interval: Time division scale numSlots: time slots execute: execute a function at a point in time The function executed in the cache is to delete the expired key, and this expiration is controlled by the timingWheel to advance the time. Next, let's learn about it through the use of timingWheel by cache. Initialization func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // Single time grid time interval ticker: ticker, // Timer, do time push, advance by interval slots: make([]*list.List, numSlots), // Time wheel timers: NewSafeMap(), // Store the map of task{key, value} [parameters needed to execute execute] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // Execution function numSlots: numSlots, // Initialize slots num setChannel: make(chan timingEntry), // The following channels are used for task delivery moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // Prepare all the lists stored in the slot tw.initSlots() // Open asynchronous coroutine, use channel for task communication and delivery go tw.run() return tw, nil } The above is a more intuitive display of the \"time wheel\" of the timingWheel, and the details of the advancement will be explained later around this picture. go tw.run() opens a coroutine for time promotion: func (tw *TimingWheel) run() { for { select { // Timer do time push -> scanAndRunTasks() case It can be seen that the timer execution is started at the time of initialization, and it is rotated in the internal time period, and then the bottom layer keeps getting the tasks from the list in the slot and handing them over to the execute for execution. Task Operation The next step is to set the cache key: func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } First look at whether this key exists in the data map If it exists, update expire -> MoveTimer() Set the key for the first time -> SetTimer() So the use of timingWheel is clear. Developers can add or update according to their needs. At the same time, when we follow the source code, we will find that: SetTimer() MoveTimer() all transports tasks to channel, and the coroutine opened in run() continuously takes out the task operations of channel. SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() From the above call chain, there is a function that will be called: moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => Get [positionEntry「pos, task」] by key val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay The delay time is less than a time grid interval, and there is no smaller scale, indicating that the task should be executed immediately if task.delay interval, the new pos, circle in the time wheel is calculated by the delay time delay pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // Move offset before and after recording. To re-enter the team for later process timer.item.diff = pos - timer.pos } else if circle > 0 { // Move to the next layer and convert circle to part of diff circle-- timer.item.circle = circle // Because it is an array, add numSlots [that is equivalent to going to the next level] timer.item.diff = tw.numSlots + pos - timer.pos } else { // If the offset is advanced, the task is still in the first layer at this time // Mark to delete the old task, and re-enter the team, waiting to be executed timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } The above process has the following situations: delay : Because For changed delay: new >= old: newCircle> 0: Calculate diff and convert circle to the next layer, so diff + numslots If only the delay time is shortened, delete the old task mark, re-add it to the list, and wait for the next loop to be executed Execute In the previous initialization, the timer in run() kept advancing, and the process of advancing was mainly to pass the tasks in the list to the executed execute func. Let's start with the execution of the timer: // Timer \"It will be executed every internal\" func (tw *TimingWheel) onTick() { // Update the current execution tick position every time it is executed tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // Get the doubly linked list of stored tasks in the tick position at this time l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } Next is how to execute execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // Store the task{key, value} that needs to be executed at present [parameters required by execute, which are passed to execute in turn] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // Mark the deletion, do the real deletion in scan \"Delete the map data\" if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // The current execution point has expired, but it is not at the first level at the same time, so now that the current level has been completed, it will drop to the next level // But did not modify pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // Because the diff has been marked before, you need to enter the queue again next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // The above cases are all cases that cannot be executed, and those that can be executed will be added to tasks tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks, and then execute each task->execute tw.runTasks(tasks) } The specific branching situation is explained in the comments. When you look at it, it can be combined with the previous moveTask(), where the circle drops, and the calculation of diff is the key to linking the two functions. As for the calculation of diff, the calculation of pos, circle is involved: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } The above process can be simplified to the following: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots Summary The timingWheel is driven by the timer. As the time advances, the tasks of the list \"doubly linked list\" in the current time grid will be taken out and passed to the execute for execution. In terms of time separation, the time wheel has circle layers, so that the original numSlots can be reused continuously, because the timer is constantly loop, and execution can drop the upper layer slot to the lower layer. You can execute the task up to the upper level continuously in the loop. There are many useful component tools in go-zero. Good use of tools is of great help to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"eco.html":{"url":"eco.html","title":"Around","keywords":"","body":"go-zero around Tools Intellij Plugin VSCode Plugin Distributed Transaction Plugins Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tool-center.html":{"url":"tool-center.html","title":"Tools","keywords":"","body":"Tools [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In go-zero, a lot of tools to improve engineering efficiency are provided, such as api and rpc generation. On this basis, the compilation of api files seems so weak. Because of the lack of highlighting, code hints, template generation, etc., this section will show you how go-zero solves these problems. This section contains the following subsections: Intellij plugin VSCode plugin Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"intellij.html":{"url":"intellij.html","title":"Intellij Plugin","keywords":"","body":"Intellij Plugin [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Go-Zero Plugin Introduction A plug-in tool that supports go-zero api language structure syntax highlighting, detection, and quick generation of api, rpc, and model. Idea version requirements IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ Features api syntax highlighting api syntax and semantic detection struct, route, handler repeated definition detection type jump to the type declaration position Support api, rpc, mode related menu options in the context menu Code formatting (option+command+L) Code hint Install The way one Find the latest zip package in the GitHub release, download it and install it locally. (No need to unzip) Thw way two In the plugin store, search for Goctl to install Preview Create a new Api(Proto) file In the project area target folder right click ->New-> New Api(Proto) File ->Empty File/Api(Proto) Template, as shown in the figure: Quickly generate api/rpc service In the target folder right click->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code generation The way one(Project Panel) Corresponding files (api, proto, sql) right click->New->Go Zero-> Api/Rpc/Model Code, as shown in the figure: Thw way two(Editor Panel) Corresponding files (api, proto, sql) right click -> Generate-> Api/Rpc/Model Code Error message Live Template Live Template can speed up our writing of api files. For example, when we enter the main keyword in the go file, press the tip and press Enter and insert a piece of template code. func main(){ } In other words, you will be more familiar with the picture below. Once upon a time, you still defined the template here. Let’s enter the instructions for using the template in today’s api grammar. Let’s take a look at the effect of the service template. First of all, in the previous picture, take a look at several template effective areas in the api file (psiTree element area) Default template and effective scope keyword psiTree effective scope description @doc ApiService doc comment template doc ApiService doc comment template struct Struct struct declaration template info ApiFile info block template type ApiFile type group template handler ApiService handler name template get ApiService get method routing template head ApiService head method routing template post ApiService post method routing template put ApiService put method routing template delete ApiService delete method routing template connect ApiService connect method routing template options ApiService options method routing template trace ApiService trace method routing template service ApiFile service service block template json Tag、Tag literal tag template xml Tag、Tag literal tag template path Tag、Tag literal tag template form Tag、Tag literal tag template For the corresponding content of each template, you can view the detailed template content in Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags, for example, the json tag template content is json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"vscode.html":{"url":"vscode.html","title":"VSCode Plugin","keywords":"","body":"vs code plugin [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The plug-in can be installed on the 1.46.0+ version of Visual Studio Code. First, please make sure that your Visual Studio Code version meets the requirements and the goctl command line tool has been installed. If Visual Studio Code is not installed, please install and open Visual Studio Code. Navigate to the \"Extensions\" pane, search for goctl and install this extension (publisher ID is \"xiaoxin-technology.goctl\"). For the extension of Visual Studio Code, please refer to here. Features Syntax highlighting Jump to definition/reference Code formatting Code block hint Syntax highlighting Jump to definition/reference Code formatting Invoke the goctl command line formatting tool, please make sure that goctl has been added to $PATH and has executable permissions before use Code block hint info block type block service block handler block Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"Distributed Transaction","keywords":"","body":"Distributed Transaction Application Scenario In a microservices architecture, when we need to ensure data consistency across services, the local database transactions are overwhelmed and cannot put multiple operations across databases and services into a single transaction. There are many such scenarios and we can list many of them. Order systems: need to ensure that order creation and stock deduction either both succeeded or both rolled back. Inter-bank transfer scenarios: the data is not in one database, but you need to ensure that the balance deduction and balance increase either both succeeded or both failed. Travel booking scenario: several tickets need to be booked in the third-party system, either all succeeded or all cancelled. For these scenarios that cannot be solved by local transactions, we need a distributed transaction solution to ensure consistency of data updates across services and databases. Solution go-zero and dtm have cooperated deeply to introduce an elegant solution that seamlessly accesses dtm in go-zero. The solution has the following features. dtm services can be directly registered to go-zero's registry through configuration go-zero is able to access the dtm server with a built-in target format dtm can recognise go-zero's target format and dynamically access services in go-zero For detailed access, see the dtm documentation: go-zero support More application scenarios dtm can address not only the above distributed transaction scenarios, but also many more scenarios related to data consistency, including Database and cache consistency: dtm's two-phase messages can guarantee atomicity for database update operations, and cache update/delete operations Flash-sales system: dtm ensures that the number of orders created in a flash-sales scenario is exactly the same as the number of stock deductions, without the need for subsequent manual calibration Multiple storage combinations: dtm already supports multiple storage such as database, Redis, Mongo, etc., which can be combined into one global transaction to ensure data consistency For more information on dtm's capabilities and presentation, see dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"plugin-center.html":{"url":"plugin-center.html","title":"Plugins","keywords":"","body":"Plugins [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The goctl api provides plugin commands to support the function extension of the api. When the functions in the goctl api do not satisfy your use, Or you need to extend goctl api function customization, then the plug-in function will be very suitable for developers to be self-sufficient, see for details goctl plugin Plugin resources goctl-go-compact Goctl's default route merges one file into one file goctl-swagger Generate swagger documents through api files goctl-php goctl-php is a plug-in based on goctl, used to generate php call end (server end) http server request code Guess you wants Plugin Commands API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"learning-resource.html":{"url":"learning-resource.html","title":"Learning Resources","keywords":"","body":"Learning Resources [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The latest learning resource channel of go-zero will be updated from time to time. Currently, the channels included are: Wechat Night OpenTalk Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"wechat.html":{"url":"wechat.html","title":"Wechat","keywords":"","body":"Wechat [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Microservices actual combat is the official official account of go-zero, where the latest go-zero best practices will be released, synchronized go night reading, go open source, GopherChina, Tencent Cloud Developer Conference and other channels about the latest go-zero Technology and information. Name Author QrCode 微服务实战 kevwan Articles(Simplified Chinese) Here are some articles. If you want to get more go-zero best practice dry goods, you can follow the public account for the latest developments. 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goreading.html":{"url":"goreading.html","title":"Night","keywords":"","body":"Night [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR 2020-08-16 XiaoHeiBan go-zero microservice framework architecture design 2020-10-03 go-zero microservice framework and online communication In-process shared calls to prevent cache breakdown Implement JWT authentication based on go-zero Goodbye go-micro! Enterprise project migration go-zero strategy (1) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gotalk.html":{"url":"gotalk.html","title":"OpenTalk","keywords":"","body":"OpenTalk [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR OpenTalk 4th - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-cache.html":{"url":"redis-cache.html","title":"Persistent layer cache","keywords":"","body":"Persistent layer cache [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Principles of Cache Design We only delete the cache without updating it. Once the data in the DB is modified, we will directly delete the corresponding cache instead of updating it. Let's see how the order of deleting the cache is correct. Delete the cache first, then update the DB Let's look at the situation of two concurrent requests. A request needs to update the data. The cache is deleted first, and then B requests to read the data. At this time, there is no data in the cache, and the data is loaded from the DB and written back to the cache, and then A updates the DB , Then the data in the cache will always be dirty data at this time, until the cache expires or there is a new data update request. As shown Update the DB first, then delete the cache A requests to update the DB first, and then B requests to read the data. At this time, the old data is returned. At this time, it can be considered that the A request has not been updated, and the final consistency is acceptable. Then A deletes the cache, and subsequent requests will Get the latest data, as shown in the figure Let's take another look at the normal request flow: The first request to update the DB and delete the cache The second request to read the cache, if there is no data, read the data from the DB and write it back to the cache All subsequent read requests can be read directly from the cache Let's take a look at the DB query, assuming that there are seven columns of ABCDEFG data in the row record: A request to query only part of the column data, such as ABC, CDE or EFG in the request, as shown in the figure Query a single complete row record, as shown in the figure Query part or all of the columns of multiple rows, as shown in the figure For the above three cases, firstly, we don’t need partial queries, because some queries cannot be cached. Once cached, the data is updated, and it is impossible to locate which data needs to be deleted; secondly, for multi-line queries, according to actual scenarios and If necessary, we will establish the corresponding mapping from the query conditions to the primary key in the business layer; and for the query of a single row of complete records, go-zero has a built-in complete cache management method. So the core principle is: go-zero cache must be a complete line record. Let's introduce in detail the cache processing methods of the three built-in scenarios in go-zero: Cache based on primary keyPRIMARY KEY (`id`) This kind of cache is relatively the easiest to handle, just use the primary key as the key in redis to cache line records. Cache based on unique index When doing index-based cache design, I used the design method of database index for reference. In database design, if you use the index to check data, the engine will first find the primary key in the tree of index -> primary key, and then use the primary key. To query row records, an indirect layer is introduced to solve the corresponding problem of index to row records. The same principle applies to the cache design of go-zero. Index-based cache is divided into single-column unique index and multi-column unique index: But for go-zero, single-column and multi-column are just different ways of generating cache keys, and the control logic behind them is the same. Then go-zero's built-in cache management can better control the data consistency problem, and also built-in to prevent the breakdown, penetration, and avalanche problems of the cache (these were discussed carefully when sharing at the gopherchina conference, see follow-up gopherchina Share video). In addition, go-zero has built-in cache access and access hit rate statistics, as shown below: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 But for go-zero, single-column and multi-column are just different ways of generating cache keys, and the control logic behind them is the same. Then go-zero's built-in cache management can better control the data consistency problem, and also built-in to prevent the breakdown, penetration, and avalanche problems of the cache (these were discussed carefully when sharing at the gopherchina conference, see follow-up gopherchina Share video). The single-column unique index is as follows: UNIQUE KEY `product_idx` (`product`) The multi-column unique index is as follows: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) Cache code interpretation 1. Cache logic based on the primary key The specific implementation code is as follows: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } The Take method here is to first get the data from the cache via the key, if you get it, return it directly, if you can't get it, then use the query method to go to the DB to read the complete row record and write it back Cache, and then return the data. The whole logic is relatively simple and easy to understand. Let's take a look at the implementation of Take in detail: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } The logic of Take is as follows: Use key to find data from cache If found, return the data If you can't find it, use the query method to read the data After reading it, call c.SetCache(key, v) to set the cache The code and explanation of doTake are as follows: // v - The data object that needs to be read // key - Cache key // query - Method used to read complete data from DB // cacheVal - Method used to write cache func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // Use barriers to prevent cache breakdown and ensure that there is only one request in a process to load the data corresponding to the key val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // Read data from the cache if err := c.doGetCache(key, v); err != nil { // If it is a placeholder that was put in beforehand (to prevent cache penetration), then the default errNotFound is returned // If it is an unknown error, then return directly, because we can't give up the cache error and directly send all requests to the DB, // This will kill the DB in a high concurrency scenario if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // request DB // If the returned error is errNotFound, then we need to set a placeholder in the cache to prevent the cache from penetrating if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // Statistics DB failed c.stat.IncrementDbFails() return nil, err } // Write data to cache if err = cacheVal(v); err != nil { logx.Error(err) } } // Return json serialized data return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // Write data to the incoming v object return jsonx.Unmarshal(val.([]byte), v) } 2. Cache logic based on unique index Because this block is more complicated, I used different colors to mark out the code block and logic of the response. block 2 is actually the same as the cache based on the primary key. Here, I mainly talk about the logic of block 1. The block 1 part of the code block is divided into two cases: The primary key can be found from the cache through the index. At this time, the primary key is used directly to walk the logic of block 2, and the follow-up is the same as the above-based primary key-based caching logic. The primary key cannot be found in the cache through the index Query the complete row record from the DB through the index, if there is an error, return After the complete row record is found, the cache of the primary key to the complete row record and the cache of the index to the primary key will be written to redis at the same time Return the required row data // v-the data object that needs to be read // key-cache key generated by index // keyer-Use the primary key to generate a key based on the primary key cache // indexQuery-method to read complete data from DB using index, need to return the primary key // primaryQuery-method to get complete data from DB with primary key func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // First query the cache through the index to see if there is a cache from the index to the primary key if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // If there is no cache of the index to the primary key, then the complete data is queried through the index primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // The complete data is queried through the index, set to “found” and used directly later, no need to read data from the cache anymore found = true // Save the mapping from the primary key to the complete data in the cache. The TakeWithExpire method has saved the mapping from the index to the primary key in the cache. return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // The data has been found through the index, just return directly if found { return nil } // Read data from the cache through the primary key, if the cache is not available, read from the DB through the primaryQuery method and write back to the cache and then return the data return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } Let's look at a practical example func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // Generate index-based keys indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // Generate a complete data cache key based on the primary key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // Index-based DB query method func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // DB query method based on primary key func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // Error handling, you need to determine whether the returned sqlc.ErrNotFound is, if it is, we use the ErrNotFound defined in this package to return // Prevent users from perceiving whether or not the cache is used, and at the same time isolate the underlying dependencies switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } All the above cache automatic management codes can be automatically generated through goctl, and the internal CRUD and cache of our team are basically automatically generated through goctl, which can save A lot of development time, and the cache code itself is also very error-prone. Even with good code experience, it is difficult to write it correctly every time. Therefore, we recommend using automatic cache code generation tools as much as possible to avoid errors. Guess you wants The fourth phase-how to design go-zero cache in OpenTalk Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"Business layer cache","keywords":"","body":"Business layer cache [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous article Persistent Layer Cache, the db layer cache was introduced. In retrospect, the main design of the db layer cache can be summarized as follows: The cache is only deleted but not updated Only one row record is always stored, that is, the row record corresponding to the primary key The unique index only caches the primary key value, not the row record directly (refer to the mysql index idea) Anti-cache penetration design, one minute by default Do not cache multi-line records Preface In a large-scale business system, by adding a cache to the persistence layer, for most single-line record queries, it is believed that the cache can help the persistence layer reduce a lot of access pressure, but in actual business, data reading is not just a single-line record. In the face of many multi-line records, this will also cause a lot of access pressure on the persistence layer. In addition, it is unrealistic to rely solely on the persistence layer for high-concurrency scenarios such as the spike system and the course selection system. In this section, we introduce the cache design in go-zero practice-biz cache. Examples of applicable scenarios subject system Content social system Spike... Like these systems, we can add another layer of cache to the business layer to store key information in the system, such as the student selection information in the course selection system, the remaining number of courses in the course selection system, and the content information during a certain period of time in the content social system. Next, let's take an example of a content social system. In the content social system, we generally query a batch of content lists first, and then click on a piece of content to view the details. Before adding biz cache, the query flowchart of content information should be: From the figure and the previous article [Persistence Layer Cache] (redis-cache.md), we can know that there is no way to get the content list to rely on the cache. If we add a layer of cache to the business layer to store the key information (or even the complete information) in the list, then access to multiple rows of records is no longer a problem, and this is what biz redis will do. Next, let’s take a look at the design plan, assuming that a single-line record in the content system contains the following fields. Field Name Field Type Remarks id string Content id title string Title content string Content createTime time.Time Create time Our goal is to obtain a batch of content lists, and try to avoid the access pressure caused by the content list going to the db. First, we use the sort set data structure of redis to store. The amount of field information that needs to be stored is based on two redis storage schemes: Cache local information The key field information (such as id, etc.) is compressed and stored according to certain rules. For score, we use the createTime millisecond value (the time value is equal, not discussed here). The advantage of this storage scheme is to save redis storage space. On the other hand, the disadvantage is that the detailed content of the list needs to be checked back again (but this back check will use the row record cache of the persistence layer) Cache complete information All published content will be stored after being compressed according to certain rules. For the same score, we still use the createTime millisecond value. The advantage of this storage solution is that business additions, deletions, checks, and changes are all redis, while the db layer is at this time. You don’t need to consider the row record cache. The persistence layer only provides data backup and recovery. On the other hand, its shortcomings are also obvious. The storage space and configuration requirements are higher, and the cost will increase. Sample code type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent provides content storage func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent provides content deletion func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // Content compression func compress(c *Content) string { // todo: do it yourself var ret string return ret } // Content decompression func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime provides data query based on time period func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } In the above example, redis does not set an expiration time. We will synchronize the add, delete, modify, and check operations to redis. We think that the content social system has a relatively high list access request to do this scheme design. In addition, there are also some data visits. I did not expect the content design system to visit so frequently. It may be a sudden increase in visits within a certain period of time, and then it may be visited again for a long time. At this interval, In other words, I will not visit again. In this scenario, how should I consider the design of the cache? In the practice of go-zero content, there are two solutions to this problem: Increased memory cache: The memory cache is used to store data that may have a large amount of sudden access. Commonly used storage schemes use map data structure to store. Map data storage is relatively simple to implement, but cache expiration processing needs to be increased The timer comes out, another solution is through Cache in the go-zero library, It is specialized Used for memory management. Use biz redis and set a reasonable expiration time Summary The above two scenarios can contain most of the multi-line record cache. For scenarios where the query volume of multi-line records is not large, there is no need to put biz redis directly in it. You can try to let db take care of it first, and developers can monitor according to the persistence layer. And service Biz needs to be introduced when monitoring to measure. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-queue.html":{"url":"go-queue.html","title":"Queue","keywords":"","body":"Queue [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the development of daily tasks, we will have many asynchronous, batch, timing, and delayed tasks to be processed. There is go-queue in go-zero. It is recommended to use go-queue for processing. Go-queue itself is also developed based on go-zero. There are two modes dq : Depends on beanstalkd, distributed, can be stored, delayed, timing settings, shutdown and restart can be re-executed, messages will not be lost, very simple to use, redis setnx is used in go-queue to ensure that each message is only consumed once, usage scenarios Mainly used for daily tasks. kq: Depends on Kafka, so I won’t introduce more about it, the famous Kafka, the usage scenario is mainly to do message queue We mainly talk about dq. The use of kq is also the same, but it depends on the bottom layer. If you haven't used beanstalkd, you can google it first. It's still very easy to use. etc/job.yaml : Configuration file Name: job Log: ServiceName: job Level: info # dq depends on Beanstalks, redis, Beanstalks configuration, redis configuration DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go: Parse dq corresponding etc/*.yaml configuration /** * @Description Configuration file * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : Responsible for multi-task registration /** * @Description Register job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) group.Start() } ProducerLogic: One of the job business logic /** * @Description Producer task * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i Another job business logic /** * @Description Consumer task * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description Configuration * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go startup file /** * @Description Startup file * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) group := service.NewServiceGroup() handler.RegisterJob(ctx,group) ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) for { s := Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"datacenter.html":{"url":"datacenter.html","title":"Middle Ground System","keywords":"","body":"How do I use go-zero to implement a Middle Ground System? [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Author: Jack Luo Original link:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] I recently discovered that a new star microservice framework has emerged in the golang community. It comes from a good future. Just looking at the name, it is very exciting. Before, I only played go-micro. In fact, I have not used it in the project. I think that microservices and grpc are very noble. They have not been in the project yet. I have really played them. I saw that the tools provided by the government are really easy to use. They only need to be defined, and the comfortable file structure is generated. I am concerned about business, and there was a voting activity recently, and in recent years, Middle Ground System have been quite popular, so I decided to give it a try. SourceCode: https://github.com/jackluo2012/datacenter Let's talk about the idea of Middle Ground System architecture first: The concept of Middle Ground System is probably to unify the apps one by one. I understand it this way anyway. Let’s talk about user service first. Now a company has a lot of official accounts, small programs, WeChat, Alipay, and xxx xxx, and a lot of platforms. Every time we develop, we always need to provide user login services. Stop copying the code, and then we are thinking about whether we can have a set of independent user services, just tell me you need to send a platform you want to log in (such as WeChat), WeChat login, what is needed is that the client returns one to the server code, and then the server takes this code to WeChat to get user information. Anyway, everyone understands it. We decided to get all the information into the configuration public service, and then store the appid and appkey of WeChat, Alipay and other platforms, as well as the appid and appkey of payment, and write a set. Finally, let's talk about implementation, the whole is a repo: Gateway, we use: go-zero's Api service Others are services, we use go-zero rpc service Look at the directory structure After the whole project was completed, I worked alone and wrote about it for a week, and then I realized the above Middle Ground System. datacenter-api service Look at the official document first https://zeromicro.github.io/go-zero/ Let's set up the gateway first:: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter View the book directory: ➜ datacenter tree . └── go.mod 0 directories, 1 file Create api file ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api └── go.mod Define api service Respectively include the above Public Service, User Service, Voting Activity Service info( title: \"demo\" desc: \"demo\" author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) // Get application information type Beid struct { Beid int64 `json:\"beid\"` } type Token struct{ Token string `json:\"token\"` } type WxTicket struct{ Ticket string `json:\"ticket\"` } type Application struct { Sname string `json:\"Sname\"` Logo string `json:\"logo\"` Isclose int64 `json:\"isclose\"` Fullwebsite string `json:\"fullwebsite\"` } type SnsReq struct{ Beid Ptyid int64 `json:\"ptyid\"` // Platform ID BackUrl string `json:\"back_url\"` // Return address after login } type SnsResp struct{ Beid Ptyid int64 `json:\"ptyid\"` // Platform ID Appid string `json:\"appid\"` // sns Platform ID Title string `json:\"title\"` LoginUrl string `json:\"login_url\"` // WeChat login address } type WxShareResp struct { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"Get information about the site\" ) @handler votesVerification get /MP_verify_NT04cqknJe0em3mT.txt (SnsReq) returns (SnsResp) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"Get social attribute information of the site\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) // Get shared returns @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"Qiniu upload credentials\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } // Registration request type RegisterReq struct { Mobile string `json:\"mobile\"` Password string `json:\"password\"` Smscode string `json:\"smscode\"` } // Login request type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` // 1. Password login, 2. SMS login Password string `json:\"password\"` } // WeChat login type WxLoginReq struct { Beid int64 `json:\"beid\"` // Application id Code string `json:\"code\"` // WeChat AccesskKey Ptyid int64 `json:\"ptyid\"` // Platform ID } //Return user information type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` // Platform ID Ptyid int64 `json:\"ptyid\"` Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service user-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service user-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } type Actid struct { Actid int64 `json:\"actid\"` } type VoteReq struct { Aeid int64 `json:\"aeid\"` Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` } type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` Descr string `json:\"descr\"` StartDate int64 `json:\"start_date\"` EnrollDate int64 `json:\"enroll_date\"` EndDate int64 `json:\"end_date\"` Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` Type int64 `json:\"type\"` Num int64 `json:\"num\"` } type EnrollReq struct { Actid Name string `json:\"name\"` Address string `json:\"address\"` Images []string `json:\"images\"` Descr string `json:\"descr\"` } type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` Name string `json:\"name\"` Address string `json:\"address\"` Images []string `json:\"images\"` Descr string `json:\"descr\"` Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` } @server( group: votes ) service votes-api { @doc( summary: \"Get activity information\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"Activity visit +1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"Get information about registered voting works\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"Get a list of registered works\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service votes-api { @doc( summary: \"vote\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } The API and document ideas that are basically written above Generate datacenter api service ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter tree . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files We open etc/datacenter-api.yaml and add the necessary configuration information Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: pass Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc I will write the above UserRpc, CommonRpc, and VotesRpc first, and then add them by step. Let's write the CommonRpc service first. CommonRpc service New project directory ➜ datacenter mkdir -p common/rpc && cd common/rpc Just create it directly in the datacenter directory, because in common, it may not only provide rpc services in the future, but also api services, so the rpc directory is added goctl create template ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto Fill in the content: ➜ rpc cat common.proto syntax = \"proto3\"; package common; option go_package = \"common\"; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } message AppConfigReq { int64 beid=1; int64 ptyid=2; } message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl generates rpc service ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files Basically, all the catalog specifications and structure are generated, so there is no need to worry about the project catalog, how to put it, and how to organize it. Take a look at the configuration information, which can write mysql and other redis information: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc Let's add database services: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files So the basic rpc is finished, and then we connect the rpc with the model and the api. This official document is already very detailed, here is just the code: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } Modify in svc: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } The above code has already associated rpc with the model database, we will now associate rpc with api: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } Join the svc service: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"timeout %s: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } Basically, we can call it in the file directory of logic: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } In this way, it is basically connected, and basically there is no need to change the others. UserRPC and VotesRPC are similar, so I won't write them here. Reviews go-zero is really fragrant, because it has a goctl tool, which can automatically generate all the code structure, we will no longer worry about the directory structure, how to organize it, and there is no architectural ability for several years It’s not easy to implement. What are the norms, concurrency, circuit breaker, no use at all, test and filter other, concentrate on realizing the business, like microservices, but also service discovery, a series of things, don’t care, because go-zero has been implemented internally. I have written code for more than 10 years. The php I have been using before, the more famous ones are laravel and thinkphp, which are basically modular. Realizations like microservices are really costly, but you use go-zero. , You develop as simple as tune api interface, other service discovery, those do not need to pay attention at all, only need to pay attention to the business. A good language, framework, and their underlying thinking are always high-efficiency and no overtime thinking. I believe that go-zero will improve the efficiency of you and your team or company. The author of go-zero said that they have a team dedicated to organizing the go-zero framework, and the purpose should be obvious, that is, to improve their own development efficiency, process flow, and standardization, which are the criteria for improving work efficiency, as we usually encounter When it comes to a problem or encounter a bug, the first thing I think of is not how to solve my bug, but whether there is a problem with my process, which of my process will cause the bug, and finally I believe in go-zeroCan become the preferred framework for microservice development. Finally, talk about the pits encountered: grpc I used grpc for the first time, and then I encountered the problem that the field value is not displayed when some characters are empty: It is realized by jsonpb in the official library of grpc. The official setting has a structure to realize the conversion of protoc buffer to JSON structure, and can configure the conversion requirements according to the fields. Cross-domain issues It is set in go-zero, and it feels no effect. The big guy said that it was set through nginx, but later found that it still didn't work. Recently, I forcibly got a domain name, and I have time to solve it later. sqlx The sqlx problem of go-zero, this really took a long time: time.Time is a data structure. Timestamp is used in the database. For example, my field is delete_at. The default database setting is null. When the result is inserted, it reports Incorrect datetime value: '0000-00-00 'for column'deleted_at' at row 1\"} This error, the query time reported deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" I removed this field decisively and added the label .omitempty above the field, which seems to be useful too, db:\".omitempty\" The second is this Conversion from collation utf8_general_ci into utf8mb4_unicode_ci. The probable reason for this is that I like to use emj expressions now, and my mysql data cannot be recognized. data links mysql still follows the original way, modify the encoding format of the configuration file, re-create the database, and set the database encoding to utf8mb4, and the sorting rule is utf8mb4_unicode_ci. In this case, all tables and string fields are in this encoding format. If you don't want all of them, you can set them separately. This is not the point. Because it is easy to set on Navicat, just click manually。 Here comes the important point: Golang uses the github.com/go-sql-driver/mysql driver, which will connect to the dsn of mysql (because I am using gorm, dsn may be different from the native format. Too the same, but it’s okay, just pay attention to charset and collation) root:password@/name?parseTime=True&loc=Local&charset=utf8 is modified to: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"stream.html":{"url":"stream.html","title":"Stream Handler","keywords":"","body":"Stream processing [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Stream processing is a computer programming paradigm that allows given a data sequence (stream processing data source), a series of data operations (functions) are applied to each element in the stream. At the same time, stream processing tools can significantly improve programmers' development efficiency, allowing them to write effective, clean, and concise code. Streaming data processing is very common in our daily work. For example, we often record many business logs in business development. These logs are usually sent to Kafka first, and then written to elasticsearch by the Job consumption Kafka, and the logs are in progress. In the process of stream processing, logs are often processed, such as filtering invalid logs, doing some calculations and recombining logs, etc. The schematic diagram is as follows: fx go-zero is a full-featured microservice framework. There are many very useful tools built in the framework, including streaming data processing tools fx , let’s use a simple example to understand the tool: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) The inputStream function simulates the generation of stream data, and the outputStream function simulates the process of stream data. The From function is the input of the stream. The Walk function concurrently acts on each item. The Filter function filters the item as true and keeps it as false. Keep, the ForEach function traverses and outputs each item element. Intermediate operations of streaming data processing There may be many intermediate operations in the data processing of a stream, and each intermediate operation can act on the stream. Just like the workers on the assembly line, each worker will return to the processed new part after operating the part, and in the same way, after the intermediate operation of the flow processing is completed, it will also return to a new flow. Intermediate operations of fx stream processing: Operation function Features Input Distinct Remove duplicate items KeyFunc, return the key that needs to be deduplicated Filter Filter items that do not meet the conditions FilterFunc, Option controls the amount of concurrency Group Group items KeyFunc, group by key Head Take out the first n items and return to the new stream int64 reserved number Map Object conversion MapFunc, Option controls the amount of concurrency Merge Merge item into slice and generate new stream Reverse Reverse item Sort Sort items LessFunc implements sorting algorithm Tail Similar to the Head function, n items form a new stream after being taken out int64 reserved number Walk Act on each item WalkFunc, Option controls the amount of concurrency The following figure shows each step and the result of each step: Usage and principle analysis From Construct a stream through the From function and return the Stream, and the stream data is stored through the channel: // Example s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter The Filter function provides the function of filtering items, FilterFunc defines the filtering logic true to retain the item, and false to not retain: // Example: Keep even numbers s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group groups the stream data. The key of the group needs to be defined. After the data is grouped, it is stored in the channel as slices: // Example Group according to the first character \"g\" or \"p\", if not, it will be divided into another group ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse can reverse the elements in the stream: // Example fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // Source Code func (p Stream) Reverse() Stream { var items []interface{} // Get the data in the stream for item := range p.source { items = append(items, item) } // Reversal algorithm for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // Write stream return Just(items...) } Distinct Distinct de-duplicates elements in the stream. De-duplication is commonly used in business development. It is often necessary to de-duplicate user IDs, etc.: // Example fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // Output: 1,2,3,4,5,6 // Source Code func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // Deduplication is performed by key, and only one of the same key is kept keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // The key is not retained if it exists if _, ok := keys[key]; !ok { source Walk The concurrency of the Walk function works on each item in the stream. You can set the number of concurrency through WithWorkers. The default number of concurrency is 16, and the minimum number of concurrency is 1. If you set unlimitedWorkers to true, the number of concurrency is unlimited, but the number of concurrent writes in the stream is unlimited. The data is limited by defaultWorkers. In WalkFunc, users can customize the elements that are subsequently written to the stream, and can write multiple elements without writing: // Example fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan Concurrent processing In addition to stream data processing, the fx tool also provides function concurrency. The realization of a function in microservices often requires multiple services. Concurrent processing dependence can effectively reduce dependency time and improve service performance. fx.Parallel(func() { userRPC() }, func() { accountRPC() }, func() { orderRPC() }) Note that when fx.Parallel performs dependency parallel processing, there will be no error return. If you need an error return, or a dependency error report needs to end the dependency request immediately, please use the MapReduce tool To process. Summary This article introduces the basic concepts of stream processing and the stream processing tool fx in go-zero. There are many stream processing scenarios in actual production. I hope this article can give you some inspiration and better response Stream processing scene at work. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"online-exchange.html":{"url":"online-exchange.html","title":"Online Exchange","keywords":"","body":"Summary of online communication issues on October 3,2020 [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Go-zero applicable scenarios I hope to talk about the application scenarios and the advantages of each scenario Highly concurrent microservice system Support tens of millions of daily activities, millions of QPS Complete microservice governance capabilities Support custom middleware Well managed database and cache Effectively isolate faults Monolithic system with low concurrency This kind of system can directly use the api layer without rpc service Use scenarios and use cases of each function Limiting Fuse Load reduction time out Observability The actual experience of go-zero The service is stable Front-end and back-end interface consistency, one api file can generate front-end and back-end code Less specification and less code means less bugs Eliminate api documents, greatly reducing communication costs The code structure is completely consistent, easy to maintain and take over Project structure of microservices, CICD processing of monorepo bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model The CI of the mono repo is made through gitlab, and the CD uses jenkins CI is as strict as possible, such as -race, using tools such as sonar CD has development, testing, pre-release, grayscale and formal clusters If it is in grayscale at 6 p.m. and there is no fault, it will automatically synchronize to the official cluster at 10 the next day The formal cluster is divided into multiple k8s clusters, which can effectively prevent a single cluster from failing, just remove it directly, and the cluster upgrade is better How to deploy and how to monitor? The full amount of K8S is automatically packaged into a docker image through jenkins, and the tag is packaged according to the time, so that you can see which day of the image is at a glance As mentioned above, pre-release -> grayscale -> formal Prometheus+ self-built dashboard service Detect service and request exceptions based on logs If you plan to change the go-zero framework to refactor your business, how can you make the online business stable and safe for users to switch without feeling? In addition, how to divide the service under consultation? Gradually replace, from outside to inside, add a proxy to proofread, you can switch after proofreading a week If there is a database reconstruction, you need to do a good job of synchronizing the old and the new Service division is based on business, following the principle of coarse to fine, avoiding one api and one microservice Data splitting is particularly important for microservices. The upper layer is easy to split, and the data is difficult to split. As far as possible, ensure that the data is split according to the business Service discovery Service discovery etcd key design Service key + timestamp, the probability of timestamp conflict in the number of service processes is extremely low, ignore it etcd service discovery and management, exception capture and exception handling Why k8s also uses etcd for service discovery, because the refresh of dns is delayed, resulting in a large number of failures in rolling updates, and etcd can achieve completely lossless updates The etcd cluster is directly deployed in the k8s cluster, because there are multiple formal clusters, clusters are single-pointed and registered to avoid confusion Automatically detect and refresh for etcd abnormalities or leader switching. When etcd has abnormalities that cannot be recovered, the service list will not be refreshed to ensure that the services are still available Cache design and use cases Distributed multiple redis clusters, dozens of largest online clusters provide caching services for the same service Seamless expansion and contraction There is no cache without expiration time to avoid a large amount of infrequently used data occupying resources, the default is one week Cache penetration, no data will be cached for one minute for a short period of time to avoid the system crashing due to interface brushing or a large number of non-existent data requests Cache breakdown, a process will only refresh the same data once, avoiding a large number of hot data being loaded at the same time Cache avalanche, automatically jitter the cache expiration time, with a standard deviation of 5%, so that the expiration time of a week is distributed within 16 hours, effectively preventing avalanches Our online database has a cache, otherwise it will not be able to support massive concurrency Automatic cache management has been built into go-zero, and code can be automatically generated through goctl Can you explain the design ideas of middleware and interceptor? Onion model This middleware processes, such as current limiting, fusing, etc., and then decides whether to call next next call Process the return result of the next call How to implement the transaction processing of microservices, the design and implementation of gozero distributed transactions, and what good middleware recommendations are there? 2PC, two-phase submission TCC, Try-Confirm-Cancel Message queue, maximum attempt Manual compensation How to design better multi-level goroutine exception capture? Microservice system request exceptions should be isolated, and a single exception request should not crash the entire process go-zero comes with RunSafe/GoSafe to prevent a single abnormal request from causing the process to crash Monitoring needs to keep up to prevent abnormal excess without knowing it The contradiction between fail fast and fault isolation Generation and use of k8s configuration (gateway, service, slb) K8s yaml file is automatically generated internally, which is too dependent on configuration and not open source I plan to add a k8s configuration template to the bookstore example slb->nginx->nodeport->api gateway->rpc service Gateway current limiting, fusing and load reduction There are two types of current limiting: concurrency control and distributed current limiting Concurrency control is used to prevent instantaneous excessive requests and protect the system from being overwhelmed Distributed current limit is used to configure different quotas for different services Fuse is to protect dependent services. When a service has a large number of exceptions, the caller should protect it so that it has a chance to return to normal and also achieve the effect of fail fast Downloading is to protect the current process from exhausting its resources and fall into complete unavailability, ensuring that the maximum amount of requests that can be carried is served as well as possible Load reduction and k8s can effectively protect k8s expansion, k8s expansion in minutes, go-zero load reduction in seconds Introduce useful components in core, such as timingwheel, etc., and talk about design ideas Bloom filter In-process cache RollingWindow TimingWheel Various executors fx package, map/reduce/filter/sort/group/distinct/head/tail... Consistent hash implementation Distributed current limiting implementation mapreduce, with cancel ability There are a lot of concurrency tools in the syncx package How to quickly add a kind of rpc protocol support, change the cross-machine discovery to the local node adjustment, and turn off the complex filter and load balancing functions go-zero has a relatively close relationship with grpc, and it did not consider supporting protocols other than grpc at the beginning of the design If you want to increase it, you can only fork out and change it. You can use the direct scheme directly by adjusting the machine Why remove filter and load balancing? If you want to go, fork is changed, but there is no need The design and implementation ideas of log and monitoring and link tracking, it is best to have a rough diagram Log and monitoring We use prometheus, customize the dashboard service, bundle and submit data (every minute) Link tracking can see the calling relationship and automatically record the trace log Is there any pooling technique useful for the go-zero framework? If so, in which core code can you refer to Generally do not need to optimize in advance, over-optimization is a taboo Core/syncx/pool.go defines a general pooling technology with expiration time Go-zero uses those performance test method frameworks, is there a code reference? You can talk about ideas and experience go benchmark Stress testing can be scaled up according to the estimated ratio by using existing business log samples The pressure test must be pressured until the system cannot be carried, see where the first bottleneck is, and then pressure again after the change, and cycle Talk about the abstract experience and experience of the code Don’t repeat yourself You may not need it. Before, business developers often asked me if I could add this function or that function. I usually ask the deep-level purpose carefully. In many cases, I find that this function is redundant, and it is the best practice to not need it. Martin Fowler proposed the principle of abstracting after three occurrences. Sometimes some colleagues will ask me to add a function to the framework. After I think about it, I often answer this. You write it in the business layer first. If there is a need in other places, you will tell me again, and it will appear three times. I will consider integrating into the framework A file should only do one thing as much as possible, each file should be controlled within 200 lines as much as possible, and a function should be controlled within 50 lines as much as possible, so that you can see the entire function without scrolling Need the ability to abstract and refine, think more, often look back and think about the previous architecture or implementation Will you publish a book on the go-zero framework from design to practice? What is the future development plan of the framework? There is no book publishing plan, and a good framework is the most important Continue to focus on engineering efficiency Improve service governance capabilities Help business development land as quickly as possible Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"contributor.html":{"url":"contributor.html","title":"Contributor","keywords":"","body":"Community contribution Author kevwan go-zero contributors kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy heyanfu sjatsh Mikaelemmmm foliet taobig Suyghur wubenqi shenbaise9527 fondoger reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl weicut zzzfwww HarryWang29 soasurs workman-Lu wuqinqiang ShyunnY appleboy Howie59 pipi-lv AlexLast almas1992 SnakeHacker sohamtembhurne toby1991 kscooo bensonfx cuishuang fyyang wangzeping722 guonaihong heyehang masonchen2014 lchjczw lucaq ahmczsy moyrne mongobaba mlboy me-cs yangwenmai magickeha lowang-bh lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang zeromake 2822132073 linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 lovelly zzhaolei zzZZzzz888 sixwaaaay liuqing6767 lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle peasfarmer qwxingzhe SeigeC jsonMark RivenChan toventang vankillua shssen rcyw weibobo windk wojiukankan wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 yiGmMk liyiwu l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen cuisongliu dahaihu dylanNew edieruby elza2 fffreedom codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog Document contributors kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:18 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"Document Contribute","keywords":"","body":"Document Contribute [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR How to contribute documents? Click the \"Edit this page\" button at the top to enter the file corresponding to the source code repository, and the developer will submit the modified (added) document in the form of pr, After we receive the pr, we will conduct a document review, and once the review is passed, the document can be updated. What documents can I contribute? Documentation errors The documentation is not standardized and incomplete Go-zero application practice and experience Component Center How soon will the document be updated after the document pr is passed? After pr accepts, github action will automatically build gitbook and release, so you can view the updated document 1-2 minutes after github action is successful. Documentation contribution notes Error correction and improvement of the source document can directly write the original md file The newly added component documents need to be typeset and easy to read, and the component documents need to be placed in the Components subdirectory Go-zero application practice sharing can be directly placed in the Development Practice subdirectory Directory structure specification The directory structure should not be too deep, preferably no more than 3 levels The component document needs to be attributed to [Component Center] (component-center.md), such as Development Practice logx bloom executors Your document directory name Application practice needs to be attributed to Development Practice, such as Development Practice [How do I use go-zero to implement a middle-office system] (datacenter.md) Stream data processing tool [Summary of online communication issues on October 3](online-exchange.md Your document directory name Development Practice Document Template # Title > Author:The author name > > Original link: The original link some markdown content Guess you wants Join Us Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error.html":{"url":"error.html","title":"Error","keywords":"","body":"Error [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Error reporting on Windows A required privilege is not held by the client. ```text Solution: \"Run as administrator\" goctl will work. ## grpc error * Case 1 ```text protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. Solution: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go installation failed go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout Please make sure GOPROXY has been set, see Go Module Configuration for GOPROXY setting api service failed to start error: config file etc/user-api.yaml, error: type mismatch for field xx Please confirm whether the configuration items in the user-api.yaml configuration file have been configured. If there are values, check whether the yaml configuration file conforms to the yaml format. command not found: goctl command not found: goctl Please make sure that goctl has been installed or whether goctl has been added to the environment variable Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"source.html":{"url":"source.html","title":"Source Code","keywords":"","body":"Source Code [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR demo Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "}} \ No newline at end of file +{"./":{"url":"./","title":"Introduction","keywords":"","body":" go-zero 0. what is go-zero go-zero is a web and rpc framework that with lots of engineering practices builtin. It’s born to ensure the stability of the busy services with resilience design, and has been serving sites with tens of millions users for years. go-zero contains simple API description syntax and code generation tool called goctl. You can generate Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript from .api files with goctl. Advantages of go-zero: improve the stability of the services with tens of millions of daily active users builtin chained timeout control, concurrency control, rate limit, adaptive circuit breaker, adaptive load shedding, even no configuration needed builtin middlewares also can be integrated into your frameworks simple API syntax, one command to generate couple of different languages auto validate the request parameters from clients plenty of builtin microservice management and concurrent toolkits 1. Backgrounds of go-zero At the beginning of 2018, we decided to re-design our system, from monolithic architecture with Java+MongoDB to microservice architecture. After researches and comparison, we chose to: Golang based great performance simple syntax proven engineering efficiency extreme deployment experience less server resource consumption Self-designed microservice architecture I have rich experience on designing microservice architectures easy to location the problems easy to extend the features 2. Design considerations on go-zero By designing the microservice architecture, we expected to ensure the stability, as well as the productivity. And from just the beginning, we have the following design principles: keep it simple high availability stable on high concurrency easy to extend resilience design, failure-oriented programming try best to be friendly to the business logic development, encapsulate the complexity one thing, one way After almost half a year, we finished the transfer from monolithic system to microservice system, and deployed on August 2018. The new system guaranteed the business growth, and the system stability. 3. The implementation and features of go-zero go-zero is a web and rpc framework that integrates lots of engineering practices. The features are mainly listed below: powerful tool included, less code to write simple interfaces fully compatible with net/http middlewares are supported, easy to extend high performance failure-oriented programming, resilience design builtin service discovery, load balancing builtin concurrency control, adaptive circuit breaker, adaptive load shedding, auto trigger, auto recover auto validation of API request parameters chained timeout control auto management of data caching call tracing, metrics and monitoring high concurrency protected As below, go-zero protects the system with couple layers and mechanisms: 4. Future development plans of go-zero auto generate API mock server, make the client debugging easier auto generate the simple integration test for the server side just from the .api files 5. Installation Run the following command under your project: go get -u github.com/zeromicro/go-zero 6. Quick Start full examples can be checked out from below: Rapid development of microservice systems Rapid development of microservice systems - multiple RPCs install goctl goctlcan be read as go control. goctl means not to be controlled by code, instead, we control it. The inside go is not golang. At the very beginning, I was expecting it to help us improve the productivity, and make our lives easier. GO111MODULE=on go get -u github.com/zeromicro/go-zero/tools/goctl make sure goctl is executable. create the API file, like greet.api, you can install the plugin of goctl in vs code, api syntax is supported. type Request struct { Name string `path:\"name,options=you|me\"` // parameters are auto validated } type Response struct { Message string `json:\"message\"` } service greet-api { @handler GreetHandler get /greet/from/:name(Request) returns (Response); } the .api files also can be generate by goctl, like below: goctl api -o greet.api generate the go server side code goctl api go -api greet.api -dir greet the generated files look like: ├── greet │ ├── etc │ │ └── greet-api.yaml // configuration file │ ├── greet.go // main file │ └── internal │ ├── config │ │ └── config.go // configuration definition │ ├── handler │ │ ├── greethandler.go // get/put/post/delete routes are defined here │ │ └── routes.go // routes list │ ├── logic │ │ └── greetlogic.go // request logic can be written here │ ├── svc │ │ └── servicecontext.go // service context, mysql/redis can be passed in here │ └── types │ └── types.go // request/response defined here └── greet.api // api description file the generated code can be run directly: cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml by default, it’s listening on port 8888, while it can be changed in configuration file. you can check it by curl: curl -i http://localhost:8888/greet/from/you the response looks like: HTTP/1.1 200 OK Date: Sun, 30 Aug 2020 15:32:35 GMT Content-Length: 0 Write the business logic code the dependencies can be passed into the logic within servicecontext.go, like mysql, reds etc. add the logic code in logic package according to .api file Generate code like Java, TypeScript, Dart, JavaScript etc. just from the api file goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 7. Benchmark Checkout the test code 8. Documents (adding) Rapid development of microservice systems Rapid development of microservice systems - multiple RPCs Examples 9. Important notes Use grpc 1.29.1, because etcd lib doesn’t support latter versions. google.golang.org/grpc v1.29.1 For protobuf compatibility, use protocol-gen@v1.3.2. go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 10. Chat group Join the chat via https://join.slack.com/t/go-zeroworkspace/shared_invite/zt-m39xssxc-kgIqERa7aVsujKNj~XuPKg Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"about-us.html":{"url":"about-us.html","title":"About Us","keywords":"","body":"About Us Go-Zero go-zero is a web and rpc framework that integrates various engineering practices. Through flexible design, the stability of the large concurrent server is guaranteed, and it has withstood full actual combat tests. go-zero contains a minimalist API definition and generation tool goctl, which can generate ready, runnable code for Go, iOS, Android, Kotlin, Dart, TypeScript, and JavaScript with one click, based on versatile api definition files. Go-Zero's Author kevwan is in charge of XiaoHeiBan’s R&D department and is a senior technical expert in the TAL Education Group. He has 14 years of R&D team management experience, 16 years of architecture design experience, and 20 years of practical engineering experience. He is responsible for the architecture design of many large-scale projects, and has consulted on many more. He has also lectured at the Gopher China Conference, and the Tencent Cloud Developer Conference. Go-Zero Members As of February 2021, go-zero currently has 30 team developers and 50+ community developers. Go-Zero Community go-zero has more than 3,000 community members. Here, you can discuss any go-zero technology, feedback on issues, get the latest go-zero information, and the technical experience shared by the big guys every day. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"join-us.html":{"url":"join-us.html","title":"Join Us","keywords":"","body":"Join Us Summary go-zero is an open-source project based on the MIT License. If you find bugs, have questions, or want to introduce new features, your contributions to the project are encouraged! We welcome your active participation and will respond to various questions, issues, and PRs as soon as possible. Contribution form Pull Request Issue Contribution notes The code in go-zero's Pull request needs to meet certain specifications: For naming conventions, please read the naming conventions doc. Code annotations should be in English where possible Pull Requests should describe the functional needs clearly and concisely Unit test coverage should be 80% at a minimum Pull Requests(PRs) Visit the go-zero project, and fork it to a new GitHub repository under your account Go back to your GitHub homepage and find the xx/go-zero project, where xx is your username. (e.g. anqiansong/go-zero) Clone the code to your local machine Develop your changes locally and then push them back to your GitHub repository From your account's go-zero project, click on Pull request in the branch info box For the base repository, choose zeromicro/go-zero with base:master. For the head repository choose your go-zero project (e.g. xx/go-zero) and compare:$branch with $branch being the new feature branch in your repository that you want merged back into the upstream project Click [Create pull request] To confirm whether the PR submitted successfully, enter the go-zero Pull requests page and look for your submission with your branch name Issues Our community will make every effort to actively provide feedback and support for problems encountered while using go-zero. However, due to the large number of people in the community both asking and answering questions, we make no guarantees about the order in which questions will be answered. Additionally, while our team is working on issues raised by the community, time constraints could cause some questions to go unanswered. One way to mitigate this is to post all feedback, be it regarding new features or discovered bugs, as a github issue for easier tracking. The go-zero team will also ensure that new features have their own issue created for easy reference by the community. The issues page is also where you can keep track of the latest go-zero news. We welcome everyone to actively participate in the issues page and community discussions. How to create a new issue Navigate to the go-zero issues page Click [New issue] in the upper right corner Choose the issue category from [Bug report], [Feature request], or [Question] Fill in the issue title and content Click [Submit new issue] Reference Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"Concepts","keywords":"","body":"Concepts go-zero go-zero is a batteries-included web and rpc framework that incorporates many known-good engineering practices. It was created with the goal of allowing developers to rapidly develop resilient services with rock-solid stability, and has been used to develop web applications with tens of millions users. goctl goctl is an auxiliary tool designed to improve engineering efficiency and reduce error rates for developers by reducing the amount of boilerplate necessary for services to communicate with each other. goctl plugins goctl has an extensible plugin system with multiple plugins already created. These plugins allow developers to generate boilerplate code and configuration for other architecture elements. Examples include goctl-go-compact which merges all goctl-generated routes into one file, the goctl-swagger plugin for generating swagger documents from api definitions, or the goctl-php plugin for generating php client code, etc. IntelliJ/Visual Studio Code plugins There are also plugins available for the IntelliJ family of IDEs as well as for Visual Studio Code. These plugins expose the CLI functions in the interface of the IDE and provide additional syntax highlighting for API definition files. API file An API file refers to the plaintext file used to define and describe an API service. It ends with the .api suffix and contains an IDL (Interface Description Language) describing the API syntax. goctl environment In order to use goctl the following is required: a golang environment protoc, the protobuf compiler the protoc-gen-go plugin a go project either using go modules or on the GOPATH If you don't have all of this setup, please visit the prepare of the documentation to get started. go-zero-demo Next up are some quickstart demos to get started with go-zero. Since these demos will be sizable after all the code is generated, it is recommended you create a go-zero-demo directory to house it all. See below for how to create this in your home directory. $ cd ~ $ mkdir go-zero-demo && cd go-zero-demo $ go mod init go-zero-demo Reference go-zero Goctl Plugins Tools API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"quick-start.html":{"url":"quick-start.html","title":"Quick Start","keywords":"","body":"Quick Start [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section mainly starts by quickly starting services such as api/rpc to let everyone have a macro concept of the project developed with go-zero, and we will introduce them in more detail in the follow-up. If you have already prepared the environment and tools with reference to prepare, please follow the following sections to start the experience: monolithic service micro service Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"Monolithic Service","keywords":"","body":"Monolithic Service [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward Since go-zero integrates web/rpc, some friends in the community will ask me whether go-zero is positioned as a microservice framework. The answer is no. Although go-zero integrates many functions, you can use any one of them independently, or you can develop a single service. It is not that every service must adopt the design of the microservice architecture. Create greet service $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. Take a look at the structure of the greet service $ cd greet $ tree . ├── etc │ └── greet-api.yaml ├── go.mod ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go It can be observed from the above directory structure that although the greet service is small, it has \"all internal organs\". Next, we can write business code in greetlogic.go. Write logic $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } Start and access the service Start service $ cd greet $ go run greet.go -f etc/greet-api.yaml Starting server at 0.0.0.0:8888... Access service $ curl -i -X GET http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} Source code greet source code Guess you wants Goctl API Directory Structure API IDL API Configuration Middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"micro-service.html":{"url":"micro-service.html","title":"Micro Service","keywords":"","body":"Microservices [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous article we have shown how to quickly create a single service, next we will show how to quickly create a microservice. In this section, the api part is actually the same logic as the monolithic service, except that there is no communication between services in the monolithic service, and the api service in the microservice will have more configuration for rpc calls. Preface This section will be a simple demonstration of an order service to call the user service, the demo code only to pass the idea, some of the links will not be enumerated. Scenario Summary Suppose we are developing a mall project, and the developer Xiaoming is responsible for the development of the user module (user) and the order module (order), let's split these two modules into two microservices ① [!NOTE] ①: The splitting of microservices is also a learning curve, so we won't discuss the details of how to split microservices here. Demonstrate functional goals Order service(order) provides a query interface user service (user) provides a method for the order service to obtain user information Service Design Analysis According to the scenario synopsis we can learn that the order is directly user-oriented, accessing data through the http protocol, and the order internal need to obtain some basic data about the user, since our service is designed using the microservices architecture. Then the two services (user,order) must exchange data, the data exchange between services that is, the communication between services, to here, the use of a reasonable communication protocol is also a developer needs to Here we choose rpc to realize the communication between services, and I believe I have already made a better scenario of \"What is the role of rpc service? I believe I have already described a good scenario here. Of course, there is much more to a service than just design analysis before development, so we won't go into detail here. From the above, we know that we need a user rpc order api two services to initially implement this little demo. Create mall project If you run the monolithic example, which is also called go-zero-demo, you may need to change the parent directory. $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo Note: If there is no cd operation to change the directory, all operations are performed in the `go-zero-demo directory Create user rpc service Create the user rpc service $ mkdir -p mall/user/rpc Add user.proto file, add getUser method $ vim mall/user/rpc/user.proto Add the following code. syntax = \"proto3\"; package user; // protoc-gen-go version is greater than 1.4.0, proto file needs to add go_package, otherwise it can't be generated option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // user id string id = 1; // user name string name = 2; // user gender string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } Generate the code $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. [!TIPS] grpc directive details refer to https://grpc.io/docs/languages/go/quickstart/ Padding business logic $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } Create order api service Create order api service # in dir go-zero-demo/mall $ mkdir -p order/api && cd order/api Add api file $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } Generate the order service $ goctl api go -api order.api -dir . Done. Add user rpc configuration $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } Add yaml configuration $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc refine the service dependencies $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } Adding order demo logic Add business logic to getorderlogic $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } Start the service and verify start etcd$ etcd download dependencies# in dir go-zero-demo $ go mod tidy start user rpc # in dir mall/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... start order api # in dir mall/order/api $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... Accessing the order api curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\": \"1\", \"name\": \"test order\"} [!TIP] The api syntax mentioned in the demo, rpc generation, goctl, goctl environment, etc. how to use and install, the quick start does not provide a detailed overview, we will have a detailed description of the subsequent documentation, you can also click the following [guess what you want to see] quick jump to see the corresponding documentation. Source Code mall source code Guess what you want to see goctl Usage Notes api directory structure introduction api syntax api configuration file introduction api middleware usage rpc directory rpc-config rpc caller description Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"framework-design.html":{"url":"framework-design.html","title":"Framework Design","keywords":"","body":"Framework Design [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section will explain the design of go-zero framework from the go-zero design philosophy and the best practice catalog of go-zero services. This section will contain the following subsections: Go-Zero Design Go-Zero Features API IDL API Directory Structure RPC Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"Go-Zero Design","keywords":"","body":"Go-Zero Design [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR For the design of the microservice framework, we expect that while ensuring the stability of microservices, we must also pay special attention to research and development efficiency. So at the beginning of the design, we have the following guidelines: Keep it simple, first principle Flexible design, fault-oriented programming Tools are bigger than conventions and documents High availability High concurrency Easy to expand Friendly to business development, package complexity There is only one way to constrain one thing Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"Go-Zero Features","keywords":"","body":"Go-Zero Features [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR go-zero is a web and rpc framework that integrates various engineering practices. It has the following main features: Powerful tool support, as little code writing as possible Minimalist interface Fully compatible with net.http Support middleware for easy expansion High performance Fault-oriented programming, flexible design Built-in service discovery, load balancing Built-in current limiting, fusing, load reduction, and automatic triggering, automatic recovery API parameter automatic verification Timeout cascade control Automatic cache control Link tracking, statistical alarm, etc. High concurrency support, stably guaranteeing daily traffic peaks during the epidemic As shown in the figure below, we have ensured the high availability of the overall service from multiple levels: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-grammar.html":{"url":"api-grammar.html","title":"API IDL","keywords":"","body":"API syntax [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR API IDL example /** * api syntax example and syntax description */ // api syntax version syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"anqiansong\" date: \"2020-01-08\" desc: \"api syntax example and syntax description\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } API syntax structure syntax statement import syntax block info syntax block type syntax block service syntax block hidden channel [!TIP] In the above grammatical structure, grammatically speaking, each grammar block can be declared anywhere in the .api file according to the grammatical block.> But in order to improve reading efficiency, we suggest to declare in the above order, because it may be in the future Strict mode is used to control the order of syntax blocks. syntax statement syntax is a newly added grammatical structure, the introduction of the grammar can solve: Quickly locate the problematic grammatical structure of the api version Syntax analysis for the version Prevent the big version upgrade of api syntax from causing backward compatibility **[!WARNING] The imported api must be consistent with the syntax version of the main api. Grammar definition 'syntax'={checkVersion(p)}STRING Grammar description syntax: Fixed token, marking the beginning of a syntax structure checkVersion: Custom go method to detect whether STRING is a legal version number. The current detection logic is that STRING must meet the regularity of (?m)\"v[1-9][0-9]\". STRING: A string of English double quotes, such as \"v1\" An api grammar file can only have 0 or 1 syntax statement. If there is no syntax, the default version is v1 Examples of correct syntax ✅ eg1: Irregular writing syntax=\"v1\" eg2: Standard writing (recommended) syntax = \"v2\" Examples of incorrect syntax ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" Import syntax block As the business scale increases, there are more and more structures and services defined in the api. All the grammatical descriptions are in one api file. This is a problem, and it will greatly increase the difficulty of reading and maintenance. Import The grammar block can help us solve this problem. By splitting the api file, different api files are declared according to certain rules, which can reduce the difficulty of reading and maintenance. **[!WARNING] Import here does not include package declarations like golang, it is just the introduction of a file path. After the final analysis, all the declarations will be gathered into a spec.Spec. You cannot import multiple identical paths, otherwise it will cause parsing errors. Grammar definition 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' Grammar description import: fixed token, marking the beginning of an import syntax checkImportValue: Custom go method to detect whether STRING is a legal file path. The current detection logic is that STRING must satisfy (?m)\"(?[az AZ 0-9_-])+\\. api\" regular. STRING: A string of English double quotes, such as \"foo.api\" Examples of correct syntax ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) Examples of incorrect syntax ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info syntax block The info grammar block is a grammar body that contains multiple key-value pairs. Its function is equivalent to the description of an api service. The parser will map it to spec.Spec for translation into other languages ​​(golang, java, etc.) Is the meta element that needs to be carried. If it is just a description of the current api, without considering its translation to other languages, you can use simple multi-line comments or java-style documentation comments. For comment descriptions, please refer to the hidden channels below. **[!WARNING] Duplicate keys cannot be used, each api file can only have 0 or 1 info syntax block Grammar definition 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' Grammar description info: fixed token, marking the beginning of an info syntax block checkKeyValue: Custom go method to check whether VALUE is a legal value. VALUE: The value corresponding to the key, which can be any character after a single line except'\\r','\\n',''. For multiple lines, please wrap it with \"\", but it is strongly recommended that everything be wrapped with \"\" Examples of correct syntax ✅ eg1:Irregular writing info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:Standard writing (recommended) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) Examples of incorrect syntax ❌ eg1:No key-value info() eg2:Does not contain colon info( foo value ) eg3:key-value does not wrap info(foo:\"value\") eg4:No key info( : \"value\" ) eg5:Illegal key info( 12: \"value\" ) eg6:Remove the old version of multi-line syntax info( foo: > some text type syntax block In the api service, we need to use a structure (class) as the carrier of the request body and the response body. Therefore, we need to declare some structures to accomplish this. The type syntax block evolved from the type of golang. Of course It also retains some of the characteristics of golang type, and the following golang characteristics are used: Keep the built-in data types of golang bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, Compatible with golang struct style declaration Keep golang keywords **[!WARNING]️ Does not support alias Does not support time.Time type, because client code is generated from API file, and not all client languages support time.Time related types Structure name, field name, cannot be a golang keyword Grammar definition Since it is similar to golang, it will not be explained in detail. Please refer to the typeSpec definition in ApiParser.g4 for the specific syntax definition. Grammar description Refer to golang writing Examples of correct syntax ✅ eg1:Irregular writing type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // Non-exported field bar int `form:\"bar\"` } type( // Non-derived structure fooBar struct{ FooBar int } ) eg2: Standard writing (recommended) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int } ) Examples of incorrect syntax ❌ eg type Gender int // not support // Non-struct token type Foo structure{ CreateTime time.Time // Does not support time.Time } // golang keyword var type var{} type Foo{ // golang keyword interface Foo interface } type Foo{ foo int // The map key must have the built-in data type of golang m map[Bar]string } [!NOTE] ① The tag definition is the same as the json tag syntax in golang. In addition to the json tag, go-zero also provides some other tags to describe the fields, See the table below for details. tag table When binding parameters, only one of the following four tags can be selected tag key Description ProviderEffective Coverage Example json Json serialization tag golang request、response json:\"fooo\" path Routing path, such as/foo/:id go-zero request path:\"id\" form Mark that the request body is a form (in the POST method) or a query (in the GET method)/search?name=keyword) go-zero request form:\"name\" header Parse values from HTTP headers, like Name: value go-zero request header:\"name\" tag modifier Common parameter verification description tag key Description Provider Effective Coverage Example optional Define the current field as an optional parameter go-zero request json:\"name,optional\" options Define the enumeration value of the current field, separated by a vertical bar | go-zero request json:\"gender,options=male\" default Define the default value of the current field go-zero request json:\"gender,default=male\" range Define the value range of the current field go-zero request json:\"age,range=[0:120]\" [!TIP] The tag modifier needs to be separated by a quotation comma after the tag value service syntax block The service syntax block is used to define api services, including service name, service metadata, middleware declaration, routing, handler, etc. **[!WARNING]️ The service name of the main api and the imported api must be the same, and there must be no ambiguity in the service name. The handler name cannot be repeated The name of the route (request method + request path) cannot be repeated The request body must be declared as a normal (non-pointer) struct, and the response body has been processed for forward compatibility. Please refer to the following description for details Grammar definition serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; Grammar description serviceSpec: Contains an optional syntax block atServer and serviceApi syntax block, which follow the sequence mode (the service must be written in order, otherwise it will be parsed incorrectly) atServer: Optional syntax block, defining server metadata of the key-value structure,'@server' indicates the beginning of this server syntax block, which can be used to describe serviceApi or route syntax block, and it has some special keys when it is used to describe different syntax blocks key needs attention,see atServerKey Key Description。 serviceApi: Contains one or more serviceRoute syntax blocks serviceRoute: Contains atDoc, handler and route in sequence mode atDoc: Optional syntax block, a key-value description of a route, which will be passed to the spec.Spec structure after parsing. If you don't care about passing it to spec.Spec, it is recommended to use a single-line comment instead. handler: It is a description of the handler layer of routing. You can specify the handler name by specifying the handler key by atServer, or you can directly use the atHandler syntax block to define the handler name atHandler: @handler fixed token, followed by a value following the regularity [_a-zA-Z][a-zA-Z_-], used to declare a handler name route: Routing consists of httpMethod, path, optional request, optional response, and httpMethod must be lowercase. body: api request body grammar definition, it must be an optional ID value wrapped by () replyBody: api response body grammar definition, must be a struct wrapped by ()、array(Forward compatible processing, it may be discarded in the future, it is strongly recommended to wrap it in struct instead of using array directly as the response body) kvLit: Same as info key-value serviceName: There can be multiple'-'join ID values path: The api request path must start with / or /:, and must not end with /. The middle can contain ID or multiple ID strings with - join atServerKey Key Description When modifying the service keyDescriptionExample jwtDeclare that all routes under the current service require jwt authentication, and code containing jwt logic will be automatically generatedjwt: Auth groupDeclare the current service or routing file groupgroup: login middlewareDeclare that the current service needs to open the middlewaremiddleware: AuthMiddleware prefixDeclare a route prefixprefix: /api When modifying the route keyDescriptionExample handlerDeclare a handler- Examples of correct syntax ✅ eg1:Irregular writing @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: /api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // Non-exported body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// Array is not recommended as response body @handler fooBar post /foo/bar (Foo) returns // You can omit 'returns' } eg2:Standard writing (recommended) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: /api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } Examples of incorrect syntax ❌ // Empty server syntax block is not supported @server( ) // Empty service syntax block is not supported service foo-api{ } service foo-api{ @doc kkkk // The short version of the doc must be enclosed in English double quotation marks @handler foo post /foo @handler foo // Duplicate handler post /bar @handler fooBar post /bar // Duplicate routing // @handler and @doc are in the wrong order @handler someHandler @doc \"some doc\" post /some/path // handler is missing post /some/path/:id @handler reqTest post /foo/req (*Foo) // Data types other than ordinary structures are not supported as the request body @handler replyTest post /foo/reply returns (*Foo) // Does not support data types other than ordinary structures and arrays (forward compatibility, later considered to be discarded) as response bodies } Hidden channel Hidden channels are currently mainly blank symbols, newline symbols and comments. Here we only talk about comments, because blank symbols and newline symbols are currently useless. Single line comment Grammar definition '//' ~[\\r\\n]* Grammar description It can be known from the grammatical definition that single-line comments must start with //, and the content cannot contain newline characters Examples of correct syntax ✅ // doc // comment Examples of incorrect syntax ❌ // break line comments java style documentation comments Grammar definition '/*' .*? '*/' Grammar description It can be known from the grammar definition that a single line comment must start with any character that starts with /* and ends with */. Examples of correct syntax ✅ /** * java-style doc */ Examples of incorrect syntax ❌ /* * java-style doc */ */ Doc&Comment If you want to get the doc or comment of a certain element, how do you define it? Doc We stipulate that the number of lines in the previous grammar block (non-hidden channel content) line+1 to all comments (the current line, or multiple lines) before the first element of the current grammar block are doc, And retain the original mark of //, /*, */. Comment We specify that a comment block (the current line, or multiple lines) at the beginning of the line where the last element of the current syntax block is located is comment, And retain the original mark of //, /*, */. Syntax block Doc and Comment support situation Syntax blockParent Syntax BlockDocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ The following is the writing of doc and comment after the corresponding syntax block is parsed // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * Enable jwt authentication */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * Route doc * Post request * Route path: foo * Request body: Foo * Response body: Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-dir.html":{"url":"api-dir.html","title":"API Directory Structure","keywords":"","body":"API directory introduction [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR . ├── etc │ └── greet-api.yaml // yaml configuration file ├── go.mod // go module file ├── greet.api // api interface description language file ├── greet.go // main function entry └── internal ├── config │ └── config.go // configuration declaration type ├── handler // routing and handler forwarding │ ├── greethandler.go │ └── routes.go ├── logic // business logic │ └── greetlogic.go ├── middleware // middleware file │ └── greetmiddleware.go ├── svc // the resource pool that logic depends on │ └── servicecontext.go └── types // The struct of request and response is automatically generated according to the api, and editing is not recommended └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"RPC Directory Structure","keywords":"","body":"RPC Directory Structure [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR proto greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [Goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations, your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. dir structure: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] The directory where the pb.go & _grpc.pb.go files are located is not fixed, it is determined by go_opt & go-grpc_opt in conjunction with the go_package value in the proto file, to understand the logic of the grpc code generation directory read Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"project-dev.html":{"url":"project-dev.html","title":"Project Development","keywords":"","body":"Project Development [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous chapters, we have introduced go-zero from the dimensions of some concepts, backgrounds, and quick start. Seeing this, I believe you already have some understanding of go-zero. From here, we will start to explain the entire process from environment preparation to service deployment. In order to ensure that everyone can thoroughly understand the go-zero development process, then prepare your patience and move on. In the chapters, the following subsections will be included: Prepare Golang Installation Go Module Configuration Goctl Installation protoc & protoc-gen-go Installation More Development Rules Naming Rules Route Rules Coding Rules Development Flow Configuration Introduction API Configuration RPC Configuration Business Development Directory Structure Model Generation API Coding Business Coding JWT Middleware RPC Implement & Call Error Handling CI/CD Service Deployment Log Collection Trace Monitor Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare.html":{"url":"prepare.html","title":"Prepare","keywords":"","body":"Prepare [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before officially entering the actual development, we need to do some preparations, such as: installation of the Go environment, installation of tools used for grpc code generation, Installation of the necessary tool Goctl, Golang environment configuration, etc., this section will contain the following subsections: Golang Installation Go Module Configuration Goctl Installation protoc & protoc-gen-go Installation More Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"golang-install.html":{"url":"golang-install.html","title":"Golang Installation","keywords":"","body":"Golang Installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward To develop a golang program, the installation of its environment must be indispensable. Here we choose to take 1.15.1 as an example. Official document https://golang.google.cn/doc/install Install Go on macOS Download and install Go for Mac Verify the installation result $ go version go version go1.15.1 darwin/amd64 Install Go on linux Download Go for Linux Unzip the compressed package to /usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz Add /usr/local/go/bin to environment variables $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile Verify the installation result $ go version go version go1.15.1 linux/amd64 Install Go on windows Download and install Go for Windows Verify the installation result $ go version go version go1.15.1 windows/amd64 More For more operating system installation, see https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gomod-config.html":{"url":"gomod-config.html","title":"Go Module Configuration","keywords":"","body":"Go Module Configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Introduction to Go Module Modules are how Go manages dependencies.[1] That is, Go Module is a way for Golang to manage dependencies, similar to Maven in Java and Gradle in Android. Module configuration Check the status of GO111MODULE $ go env GO111MODULE on Turn on GO111MODULE, if it is already turned on (that is, execute go env GO111MODULE and the result is on), please skip it. $ go env -w GO111MODULE=\"on\" Set up GOPROXY $ go env -w GOPROXY=https://goproxy.cn Set up GOMODCACHE view GOMODCACHE $ go env GOMODCACHE If the directory is not empty or /dev/null, please skip it. go env -w GOMODCACHE=$GOPATH/pkg/mod Reference [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-install.html":{"url":"goctl-install.html","title":"Goctl Installation","keywords":"","body":"Goctl Installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Foreword Goctl plays a very important role in the development of the go-zero project. It can effectively help developers greatly improve development efficiency, reduce code error rate, and shorten the workload of business development. For more introductions to Goctl, please read Goctl Introduction , Here we strongly recommend that you install it, because most of the follow-up demonstration examples will use goctl for demonstration. Install(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 go install github.com/zeromicro/go-zero/tools/goctl@latest Environmental variable detection The compiled binary file downloaded by go get is located in the $GOPATH/bin directory. Make sure that $GOPATH/bin has been added to the environment variable. $ sudo vim /etc/paths Add the following in the last line $GOPATH/bin [!TIP] $GOPATH is the filepath on your local machine Installation result verification $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] For windows users to add environment variables, please Google by yourself. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go Installation","keywords":"","body":"protoc & protoc-gen-go installation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Forward protoc is a tool written in C++, which can translate proto files into codes in the specified language. In the go-zero microservices, we use grpc to communicate between services, and the writing of grpc requires the use of protoc and the plug-in protoc-gen-go that translates into go language rpc stub code. Demonstration environment of this document mac OS way 1: install from goctl $ goctl env check -i -f -v [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! way2: install from web page protoc installation Enter the protobuf release page and select the compressed package file suitable for your operating system Unzip protoc-3.14.0-osx-x86_64.zip and enter protoc-3.14.0-osx-x86_64 $ cd protoc-3.14.0-osx-x86_64/bin Move the started protoc binary file to any path added to the environment variable, such as $GOPATH/bin. It is not recommended putting it directly with the next path of the system. $ mv protoc $GOPATH/bin [!TIP] $GOPATH is the actual folder address of your local machine Verify the installation result $ protoc --version libprotoc 3.14.0 protoc-gen-* installation [!TIPS] Windows may report an error, A required privilege is not held by the client., because goctl needs to be run as administrator under Windows. The reason is that goctl needs to be run \"as administrator\" under Windows. Download and install protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest **[!WARNING] protoc-gen-go installation failed, please read Error Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare-other.html":{"url":"prepare-other.html","title":"More","keywords":"","body":"Other [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before, we have prepared the Go environment, Go Module configuration, Goctl, protoc & protoc-gen-go installation, these are the environments that developers must prepare during the development phase, and you can optionally install the next environment, Because these environments generally exist on the server (installation work, operation and maintenance will be completed for you), but in order to complete the follow-up demonstration process, I suggest you install it locally, because most of our demo environments will be Locally based. The following only gives the necessary preparatory work, and does not give a detailed introduction in the length of the document. Other environment etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-specification.html":{"url":"dev-specification.html","title":"Development Rules","keywords":"","body":"Development Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In actual business development, in addition to improving business development efficiency, shortening business development cycles, and ensuring high performance and high availability indicators for online business, good programming habits are also one of the basic qualities of a developer. In this chapter, We will introduce the coding standards in go-zero. This chapter is an optional chapter. The content is for communication and reference only. This chapter will explain from the following subsections: Naming Rules Route Rules Coding Rules Three principles of development Clarity The author quoted a quote from Hal Abelson and Gerald Sussman: Programs must be written for people to read, and only incidentally for machines to execute Simplicity Simplicity is prerequisite for reliability Edsger W. Dijkstra believes that: the prerequisite for reliability is simplicity. We have all encountered in actual development. What is this code written and what it wants to accomplish. Developers don’t understand this code, so they don’t know. How to maintain, this brings complexity, the more complex the program, the harder it is to maintain, and the harder it is to maintain, the program becomes more and more complicated. Therefore, the first thing you should think of when encountering a program becoming complicated is - Refactoring, refactoring will redesign the program and make the program simple. Productivity) In the go-zero team, this topic has always been emphasized. The productivity of developers is not how many lines of code you have written and how many module developments you have completed, but we need to use various effective ways to take advantage of the limited Time to complete the development to maximize the efficiency, and the birth of Goctl was officially to increase productivity, Therefore, I very much agree with this development principle. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"naming-spec.html":{"url":"naming-spec.html","title":"Naming Rules","keywords":"","body":"Naming Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In any language development, there are some naming conventions in the language field, good Reduce code reading costs Reduce maintenance difficulty Reduce code complexity Specification suggestion In our actual development, many developers may transfer from one language to another language field. After switching to another language, We will all retain the programming habits of the old language. Here, what I suggest is that although some previous specifications of different languages may be the same, But we'd better be familiar with some official demos to gradually adapt to the programming specifications of the current language, rather than directly migrating the programming specifications of the original language. Naming guidelines When the distance between the definition and the last use of the variable name is short, the short name looks better. Variable naming should try to describe its content, not type Constant naming should try to describe its value, not how to use this value When encountering for, if and other loops or branches, single letter names are recommended to identify parameters and return values It is recommended to use words to name method, interface, type, and package The package name is also part of the naming, please use it as much as possible Use a consistent naming style File naming guidelines All lowercase Avoid underscores (_) except for unit test The file name should not be too long Variable naming convention reference Initial lowercase Hump naming See the name to know the meaning, avoid pinyin instead of English It is not recommended including an underscore (_) It is not recommended including numbers Scope of application Local variables Function parameter output, input parameter Function and constant naming convention Camel case naming The first letter of the exported must be capitalized The first letter must be lowercase if it cannot be exported Avoid the combination of all uppercase and underscore (_) [!TIP] If it is a go-zero code contribution, you must strictly follow this naming convention Reference Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"Route Rules","keywords":"","body":"Route Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Recommended spine naming Combinations of lowercase words and horizontal bars (-) What you see is what you get /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"coding-spec.html":{"url":"coding-spec.html","title":"Coding Rules","keywords":"","body":"Coding Rules [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR import Single-line import is not recommended being wrapped in parentheses Introduce in the order of Official Package, NEW LINE, Project Package, NEW LINE, Third Party Dependent Package import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) Function returns Object avoids non-pointer return Follow the principle that if there is a normal value return, there must be no error, and if there is an error, there must be no normal value return. Error handling An error must be handled, if it cannot be handled, it must be thrown. Avoid underscore (_) receiving error Function body coding It is recommended that a block end with a blank line, such as if, for, etc. func main (){ if x==1{ // do something } fmt.println(\"xxx\") } Blank line before return func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-flow.html":{"url":"dev-flow.html","title":"Development Flow","keywords":"","body":"Development Flow [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The development process here is not a concept with our actual business development process. The definition here is limited to the use of go-zero, that is, the development details at the code level. Development Flow Goctl environment preparation [1] Database Design Business development New Construction Create service catalog Create service type (api/rpc/rmq/job/script) Write api and proto files Code generation Generate database access layer code model Configuration config, yaml change Resource dependency filling (ServiceContext) Add middleware Business code filling Error handling [!TIP] [1] goctl environment Development Tools Visual Studio Code Goland (recommended) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"config-introduction.html":{"url":"config-introduction.html","title":"Configuration Introduction","keywords":"","body":"Configuration Introduction [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Before officially using go-zero, let us first understand the configuration definitions of different service types in go-zero, and see what role each field in the configuration has. This section will contain the following subsections: API Configuration RPC Configuration Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-config.html":{"url":"api-config.html","title":"API Configuration","keywords":"","body":"API configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The api configuration controls various functions in the api service, including but not limited to the service listening address, port, environment configuration, log configuration, etc. Let's take a simple configuration to see what the common configurations in the api do. Configuration instructions Through the yaml configuration, we will find that there are many parameters that we are not aligned with config. This is because many of the config definitions are labeled with optional or default. For optional options, you can choose according to your own Need to determine whether it needs to be set. For the default tag, if you think the default value is enough, you don't need to set it. Generally, the value in default basically does not need to be modified and can be considered as a best practice value. Config type Config struct{ rest.RestConf // rest api configuration Auth struct { // jwt authentication configuration AccessSecret string // jwt key AccessExpire int64 // jwt expire, unit: second } Mysql struct { // database configuration, in addition to mysql, there may be other databases such as mongo DataSource string // mysql datasource, which satisfies the format of user:password@tcp(ip:port)db?queries } CacheRedis cache.CacheConf // redis cache UserRpc zrpc.RpcClientConf // rpc client configuration } rest.RestConf The basic configuration of api service, including monitoring address, monitoring port, certificate configuration, current limit, fusing parameters, timeout parameters and other controls, expand it, we can see: service.ServiceConf // service configuration Host string `json:\",default=0.0.0.0\"` // http listening ip, default 0.0.0.0 Port int // http listening port, required CertFile string `json:\",optional\"` // https certificate file, optional KeyFile string `json:\",optional\"` // https private key file, optional Verbose bool `json:\",optional\"` // whether to print detailed http request log MaxConns int `json:\",default=10000\"` // http can accept the maximum number of requests at the same time (current limit), the default is 10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http can accept the maximum Content Length of the request, the default is 1048576, and the set value cannot be between 0 and 8388608 // milliseconds Timeout int64 `json:\",default=3000\"` // timeout duration control, unit: milliseconds, default 3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // CPU load reduction threshold, the default is 900, the allowable setting range is 0 to 1000 Signature SignatureConf `json:\",optional\"` // signature configuration service.ServiceConf type ServiceConf struct { Name string // service name Log logx.LogConf // log configuration Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // service environment, dev-development environment, test-test environment, pre-pre-release environment, pro-formal environment MetricsUrl string `json:\",optional\"` // index report interface address, this address needs to support post json Prometheus prometheus.Config `json:\",optional\"` // prometheus configuration } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // service name Mode string `json:\",default=console,options=console|file|volume\"` // Log mode, console-output to console, file-output to the current server (container) file, volume-output docker hangs in the file Path string `json:\",default=logs\"` // Log storage path Level string `json:\",default=info,options=info|error|severe\"` // Log level Compress bool `json:\",optional\"` // whether to enable gzip compression KeepDays int `json:\",optional\"` // log retention days StackCooldownMillis int `json:\",default=100\"` // log write interval } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus monitor host Port int `json:\",default=9101\"` // prometheus listening port Path string `json:\",default=/metrics\"` // report address } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // Whether it is Strict mode, if it is, Private Keys is required Expiry time.Duration `json:\",default=1h\"` // Validity period, default is 1 hour PrivateKeys []PrivateKeyConf // Signing key related configuration } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // Fingerprint configuration KeyFile string // Key configuration } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // Weights } redis.RedisConf RedisConf struct { Host string // redis address Type string `json:\",default=node,options=node|cluster\"` // redis type Pass string `json:\",optional\"` // redis password } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-config.html":{"url":"rpc-config.html","title":"RPC Configuration","keywords":"","body":"RPC Configuration [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The rpc configuration controls various functions of an rpc service, including but not limited to listening address, etcd configuration, timeout, fuse configuration, etc. Below we will use a common rpc service configuration to illustrate. Configuration instructions Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // Redis cache configuration, see the api configuration instructions for details, and I won’t go into details here Mysql struct { // mysql database access configuration, see api configuration instructions for details, not repeat here DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // mysql database access configuration, see api configuration instructions for details, not repeat here ListenOn string // rpc listening address and port, such as: 127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd related configuration Auth bool `json:\",optional\"` // Whether to enable Auth, if yes, Redis is required Redis redis.RedisKeyConf `json:\",optional\"` // Auth verification StrictControl bool `json:\",optional\"` // Whether it is Strict mode, if it is, the error is Auth failure, otherwise it can be considered as successful // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // Timeout control, unit: milliseconds CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // CPU load reduction threshold, the default is 900, the allowable setting range is 0 to 1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host array Key string // rpc registration key } redis.RedisKeyConf RedisConf struct { Host string // redis host Type string `json:\",default=node,options=node|cluster\"` // redis type Pass string `json:\",optional\"` // redis password } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // Verification key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-dev.html":{"url":"business-dev.html","title":"Business Development","keywords":"","body":"Business development [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In this chapter, we use a simple example to demonstrate some basic functions in go-zero. This section will contain the following subsections: Directory Structure Model Generation API Coding Business Coding JWT Middleware RPC Implement & Call Error Handling Demo project download Before officially entering the follow-up document description, you can pay attention to the source code here, and we will perform a progressive demonstration of the function based on this source code. Instead of starting from 0 completely, if you come from the Quick Start chapter, this source code structure is not a problem for you. Click Here to download Demo project Demonstration project description Scenes The programmer Xiao Ming needs to borrow a copy of \"Journey to the West\". When there is no online library management system, he goes to the front desk of the library to consult with the librarian every day. Xiao Ming: Hello, do you still have the book \"Journey to the West\" today? Administrator: No more, let's check again tomorrow. One day later, Xiao Ming came to the library again and asked: Xiao Ming: Hello, do you still have the book \"Journey to the West\" today? Administrator: No, you can check again in two days. After many repetitions in this way, Xiao Ming was also in vain and wasted a lot of time on the way back and forth, so he finally couldn't stand the backward library management system. He decided to build a book review system by himself. Expected achievement User login: Rely on existing student system data to log in Book search: Search for books based on book keywords and query the remaining number of books. System analysis Service design user api: provides user login protocol rpc: for search service to access user data search api: provide book query agreement [!TIP] Although this tiny book borrowing query system is small, it does not fit the business scenario in practice, but only the above two functions have already met our demonstration of the go-zero api/rpc scenario. In order to satisfy the richer go-zero function demonstration in the future, business insertion, that is, related function descriptions, will be carried out in the document. Here only one scene is used for introduction. NOTE: Please create the sql statement in the user into the db by yourself, see prepare for more preparation work Add some preset user data to the database for later use. For the sake of space, the demonstration project does not demonstrate the operation of inserting data in detail. Reference preset data INSERT INTO `user` (number,name,password,gender)values ('666','xiaoming','123456','male'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-design.html":{"url":"service-design.html","title":"Directory Structure","keywords":"","body":"Directory Structure [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Directory splitting refers to the directory splitting in line with the best practices of go-zero, which is related to the splitting of microservices. In the best practice within the team, We split a system into multiple subsystems according to the horizontal division of the business, and each subsystem should have an independent persistent storage and cache system. For example, a shopping mall system needs to consist of a user system (user), a product management system (product), an order system (order), a shopping cart system (cart), a settlement center system (pay), an after-sale system (afterSale), etc. System structure analysis In the mall system mentioned above, while each system provides services to the outside (http), it also provides data to other subsystems for data access interfaces (rpc), so each subsystem can be split into a service , And provides two external ways to access the system, api and rpc, therefore, The above system is divided into the following structure according to the directory structure: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc call chain suggestion When designing the system, try to make the call between services one-way in the chain instead of cyclically. For example, the order service calls the user service, and the user service does not call the order service in turn. When one of the services fails to start, it will affect each other and enter an infinite loop. You order to think it is caused by the user service failure, and the user thinks it is caused by the order service. If there are a large number of services in a mutual call chain, You need to consider whether the service split is reasonable. Directory structure of common service types Among the above services, only api/rpc services are listed. In addition, there may be more service types under one service, such as rmq (message processing system), cron (timed task system), script (script), etc. , Therefore, a service may contain the following directory structure: user ├── api // http access service, business requirement realization ├── cronjob // Timed task, timed data update service ├── rmq // Message processing system: mq and dq, handle some high concurrency and delay message services ├── rpc // rpc service to provide basic data access to other subsystems └── script // Script, handle some temporary operation requirements, and repair temporary data Example of complete project directory structure mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script Guess you wants API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"model-gen.html":{"url":"model-gen.html","title":"Model Generation","keywords":"","body":"Model Generation [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR First, after downloading the demo project, we will use the user's model to demonstrate the code generation. Forward Model is a bridge for services to access the persistent data layer. The persistent data of the business often exists in databases such as mysql and mongo. We all know that the operation of a database is nothing more than CURD. And these tasks will also take up part of the time for development. I once wrote 40 model files when writing a business. According to the complexity of different business requirements, on average, each model file is almost required. 10 minutes, for 40 files, 400 minutes of working time, almost a day's workload, and the goctl tool can complete the 400 minutes of work in 10 seconds. Prepare Enter the demo project book, find theuser.sql file under user/model, and execute the table creation in your own database. Code generation (with cache) The way one(ddl) Enter the service/user/model directory and execute the command $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. The way two(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource is the database connection address The way three(intellij plugin) In Goland, right-click user.sql, enter and click New->Go Zero->Model Code to generate it, or open the user.sql file, Enter the editing area, use the shortcut key Command+N (for macOS) or alt+insert (for windows), select Mode Code. [!TIP] The intellij plug-in generation needs to install the goctl plug-in, see intellij plugin for details Verify the generated model file view tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go More For persisting data, if more flexible database capabilities are required, including transactional capabilities, see Mysql For distributed transaction capabilities, see distributed-transaction.md Guess you wants Model Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-coding.html":{"url":"api-coding.html","title":"API Coding","keywords":"","body":"API File Coding [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Create file $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } Generate api service By goctl executable file $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. By Intellij Plugin Right-click on the user.api file, and then click to enter New->Go Zero->Api Code, enter the target directory selection, that is, the target storage directory of the api source code, the default is the directory where user.api is located, select Click OK after finishing the list. By Keymap Open user.api, enter the editing area, use the shortcut key Command+N (for macOS) or alt+insert (for windows), select Api Code, and also enter the directory selection pop-up window, after selecting the directory Just click OK. Guess you wants API IDL API Commands API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-coding.html":{"url":"business-coding.html","title":"Business Coding","keywords":"","body":"Business code [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous section, we have written user.api based on the preliminary requirements to describe which services the user service provides to the outside world. In this section, we will continue with the previous steps. Use business coding to tell how go-zero is used in actual business. Add Mysql configuration $ vim service/user/api/internal/config/config.go package config import \"github.com/zeromicro/go-zero/rest\" type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } Improve yaml configuration $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql database user $password: mysql database password $url: mysql database connection address $db: mysql database db name, that is, the database where the user table is located $host: Redis connection address Format: ip:port, such as: 127.0.0.1:6379 $pass: redis password For more configuration information, please refer to api configuration introduction Improve service dependence $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } Fill in the login logic $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"Invalid parameter\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"Username does not exist\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"User password is incorrect\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] For the code implementation of [start]-[end] in the above code, please refer to the Jwt Authentication chapter Guess you wants API IDL API Commands API Directory Structure JWT API Configuration Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"jwt.html":{"url":"jwt.html","title":"JWT","keywords":"","body":"JWT authentication [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Summary JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and independent method for securely transmitting information as JSON objects between parties. Since this information is digitally signed, it can be verified and trusted. The JWT can be signed using a secret (using the HMAC algorithm) or using a public/private key pair of RSA or ECDSA. When should you use JSON Web Tokens? Authorization: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains. Information exchange: JSON Web Tokens are a good way of securely transmitting information between parties. Because JWTs can be signed—for example, using public/private key pairs—you can be sure the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content hasn't been tampered with. Why should we use JSON Web Tokens? As JSON is less verbose than XML, when it is encoded its size is also smaller, making JWT more compact than SAML. This makes JWT a good choice to be passed in HTML and HTTP environments. Security-wise, SWT can only be symmetrically signed by a shared secret using the HMAC algorithm. However, JWT and SAML tokens can use a public/private key pair in the form of a X.509 certificate for signing. Signing XML with XML Digital Signature without introducing obscure security holes is very difficult when compared to the simplicity of signing JSON. JSON parsers are common in most programming languages because they map directly to objects. Conversely, XML doesn't have a natural document-to-object mapping. This makes it easier to work with JWT than SAML assertions. Regarding usage, JWT is used at Internet scale. This highlights the ease of client-side processing of the JSON Web token on multiple platforms, especially mobile. [!TIP] All the above content quote from jwt.io How to use jwt in go-zero Jwt authentication is generally used at the api layer. In this demonstration project, we generate jwt token when user api logs in, and verify the user jwt token when searching api for books. user api generates jwt token Following the content of the Business Coding chapter, we perfect the getJwtToken method left over from the previous section, that is, generate the jwt token logic Add configuration definition and yaml configuration items $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret: The easiest way to generate the key of the jwt token is to use an uuid value. $AccessExpire: Jwt token validity period, unit: second For more configuration information, please refer to API Configuration $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search.api uses jwt token authentication Write search.api file $ vim service/search/api/search.api type ( SearchReq { Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth: Enable jwt authentication If the routing requires JWT authentication, you need to declare this syntax flag above the service, such as /search/do above Routes that do not require jwt authentication do not need to be declared, such as /search/ping above For more grammar, please read API IDL Generate code As described above, there are three ways to generate code, so I won’t go into details here. Add yaml configuration items $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret: This value must be consistent with the one declared in the user api. $AccessExpire: Valid period Modify the port here to avoid conflicts with user api port 8888 Verify jwt token Start user api service, and login $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"xiaoming\",\"gender\":\"male\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} Start the search api service, call /search/do to verify whether the jwt authentication is passed $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... Let’s not pass the jwt token and see the result: $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 Obviously, the jwt authentication failed, and the statusCode of 401 is returned. Next, let's take a jwt token (that is, the accessToken returned by the user login) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] Service startup error, please check Error At this point, the demonstration of jwt from generation to use is complete. The authentication of jwt token is already encapsulated in go-zero. You only need to simply declare it when defining the service in the api file. Get the information carried in the jwt token After go-zero is parsed from the jwt token, the kv passed in when the user generates the token will be placed in the Context of http.Request intact, so we can get the value you want through the Context. $ vim /service/search/api/internal/logic/searchlogic.go Add a log to output the userId parsed from jwt. func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } Output {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} Guess you wants JWT API Configuration API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"middleware.html":{"url":"middleware.html","title":"Middleware","keywords":"","body":"Middleware [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous section, we demonstrated how to use jwt authentication. I believe you have mastered the basic use of jwt. In this section, let’s take a look at how to use api service middleware. Middleware classification In go-zero, middleware can be divided into routing middleware and global middleware. Routing middleware refers to certain specific routes that need to implement middleware logic, which is similar to jwt and does not place the routes under jwt:xxx Does not use middleware functions, The service scope of global middleware is the entire service. Middleware use Here we take the search service as an example to demonstrate the use of middleware Routing middleware Rewrite the search.api file and add the middleware declaration $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // Routing middleware declaration ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } Regenerate the api code $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. After the generation is completed, there will be an additional middleware directory under the internal directory, which is the middleware file, and the implementation logic of the subsequent middleware is also written here. Improve resource dependency ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } Write middleware logic Only one line of log is added here, with the content example middle. If the service runs and outputs example middle, it means that the middleware is in use. $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } Start service verification {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} Global middleware call rest.Server.Use func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // Global middleware server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} Call another service within the middleware Pass another service into the middleware by closure, example as below: // simulated another service type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // regular middleware func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // the middleware that calls another service func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } For full example, see: https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-call.html":{"url":"rpc-call.html","title":"RPC Implement & Call","keywords":"","body":"RPC Implement & Call [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In a large system, there must be data transfer between multiple subsystems (services). If there is data transfer, you need a communication method. You can choose the simplest http for communication or rpc service for communication. In go-zero, we use zrpc to communicate between services, which is based on grpc. Scenes In the front, we have improved the interface protocol for user login, user query of books, etc., but the user did not do any user verification when querying the book. If the current user is a non-existent user, we do not allow him to view book information. From the above information, we can know that the user service needs to provide a method to obtain user information for use by the search service, so we need to create a user rpc service and provide a getUser method. rpc service writing Compile the proto file $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } Generate rpc service code$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] If the installed version of protoc-gen-go is larger than 1.4.0, it is recommended to add go_package to the proto file Add configuration and improve yaml configuration items $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql database user $password: mysql database password $url: mysql database connection address $db: mysql database db name, that is, the database where the user table is located $host: Redis connection address Format: ip:port, such as: 127.0.0.1:6379 $pass: redis password $etcdHost: etcd connection address, format: ip:port, such as: 127.0.0.1:2379 For more configuration information, please refer to rpc configuration introduction Add resource dependency $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } Add rpc logic $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } Use rpc Next we call user rpc in the search service Add UserRpc configuration and yaml configuration items $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret: This value must be consistent with the one declared in the user api. $AccessExpire: Valid period $etcdHost: etcd connection address The Key in etcd must be consistent with the Key in the user rpc service configuration Add dependency $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } Supplementary logic $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // use user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } Start and verify the service Start etcd, redis, mysql Start user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... Start search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml Verification Service $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"xiyouji\",\"count\":100} Guess you wants RPC Configuration RPC Directory Structure RPC Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error-handle.html":{"url":"error-handle.html","title":"Error Handling","keywords":"","body":"Error Handling [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Error handling is an indispensable part of service. In normal business development, we can think that the http status code is not in the 2xx series, it can be regarded as an http request error. It is accompanied by error messages in response, but these error messages are all returned in plain text. In addition, I will define some business errors in the business, and the common practice is to pass The two fields code and msg are used to describe the business processing results, and it is hoped that the response can be made with the json response body. Business error response format Business processing is normal { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } Business processing exception { \"code\": 10001, \"msg\": \"something wrong\" } login of user api Previously, when we handled the login logic when the username did not exist, an error was directly returned. Let's log in and pass a username that does not exist to see the effect. curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 Username does not exist Next we will return it in json format Custom error First add a baseerror.go file in common and fill in the code $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } Replace errors in login logic with CodeError custom errors if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"Invalid parameter\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"Username does not exist\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"User password is incorrect\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil Use custom errors $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // Custom error httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } Restart service verification $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"Username does not exist\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In software engineering, CI/CD or CICD generally refers to the combined practices of continuous integration and either continuous delivery or continuous deployment. ——Wikipedia What can CI do? In modern application development, the goal is to have multiple developers working simultaneously on different features of the same app. However, if an organization is set up to merge all branching source code together on one day (known as “merge day”), the resulting work can be tedious, manual, and time-intensive. That’s because when a developer working in isolation makes a change to an application, there’s a chance it will conflict with different changes being simultaneously made by other developers. This problem can be further compounded if each developer has customized their own local integrated development environment (IDE), rather than the team agreeing on one cloud-based IDE. ——Continuous integration From a conceptual point of view, CI/CD includes the deployment process. Here, we will put the deployment (CD) in a separate section Service Deployment, This section uses gitlab to do a simple CI (Run Unit Test) demonstration. Gitlab CI Gitlab CI/CD is a built-in software development tool of Gitlab, providing Continuous Integration (CI) Continuous Delivery (CD) Continuous deployment (CD) Prepare gitlab installation git installation gitlab runner installation Enable Gitlab CI Upload code Create a new warehouse go-zero-demo in gitlab Upload the local code to the go-zero-demo warehouse Create a .gitlab-ci.yaml file in the project root directory. Through this file, a pipeline can be created, which will be run when there is a content change in the code repository. The pipeline is run in sequence by one or more. Each stage can contain one or more jobs running in parallel. Add CI content (for reference only) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] The above CI is a simple demonstration. For detailed gitlab CI, please refer to the official gitlab documentation for richer CI integration. Reference CI/CD Wikipedia Continuous integration Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-deployment.html":{"url":"service-deployment.html","title":"Service Deployment","keywords":"","body":"Service Deployment [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section uses jenkins to demonstrate a simple service deployment to k8s. Prepare k8s cluster installation gitlab environment installation jenkins environment installation redis&mysql&nginx&etcd installation goctl install [!TIP] Ensure that goctl is installed on each node of k8s Please google for the installation of the above environment, and I will not introduce it here. Service deployment 1、Relevant preparations for gitlab code warehouse 1.1、Add SSH Key Enter gitlab, click on the user center, find Settings, find the SSH Keys tab on the left 1、View the public key on the machine where jenkins is located $ cat ~/.ssh/id_rsa.pub 2、If not, you need to generate it, if it exists, please skip to step 3 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 After completing the generation, repeat the first step 3、Add the public key to gitlab 1.2、Upload the code to the gitlab warehouse Create a new project go-zero-demo and upload the code. Details are not described here. 2、jenkins 2.1、Add credentials View the private key of the machine where Jenkins is located, which corresponds to the previous gitlab public key $ cat id_rsa Enter jenkins, click on Manage Jenkins-> Manage Credentials Go to the Global Credentials page, add credentials, Username is an identifier, add pipeline later, you know that this identifier represents the credentials of gitlab, and Private Key` is the private key obtained above 2.2、 Add global variables Enter Manage Jenkins->Configure System, slide to the entry of Global Properties, add docker private warehouse related information, as shown in the figure is docker username, docker user password, docker private warehouse address [!TIP] docker_user your docker username docker_pass your docker user password docker_server your docker server The private warehouse I use here, if you don’t use the private warehouse provided by the cloud vendor, you can build a private warehouse yourself. I won’t go into details here, and you can google it yourself. 2.3、Configure git Go to Manage Jenkins->Global Tool Configureation, find the Git entry, fill in the path of the git executable file of the machine where jenkins is located, if not, you need to download the Git plugin in the jenkins plugin management. 2.4、 Add a pipeline The pipeline is used to build the project, pull code from gitlab->generate Dockerfile->deploy to k8s are all done in this step, here is the demo environment, in order to ensure the smooth deployment process, Need to install jenkins on the machine where one of the nodes of the k8s cluster is located, I installed it on the master here. Get the credential id Go to the credential page and find the credential id whose Username is gitlab Go to the jenkins homepage, click on New Item, the name is user View project git address Add the service type Choice Parameter, check This project is parameterized in General, Click Add parameter and select Choice Parameter, add the selected value constant (api, rpc) and the variable (type) of the received value according to the figure, which will be used in the Pipeline script later. Configure user, on the user configuration page, swipe down to find Pipeline script, fill in the script content pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: 'Select the branch' } stages { stage('service info') { steps { sh 'echo branch: $branch' sh 'echo build service type:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('get commit_id') { steps { echo 'get commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl version detection') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo image:${image}' sh 'docker build -t ${image} .' } } stage('Upload to the mirror warehouse') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('Deploy to k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId} should be replaced with your specific credential value, that is, a string of strings in the [Add Credentials] module, ${gitUrl} needs to be replaced with the git warehouse address of your code, other variables in the form of ${xxx} are not required Modify it and keep it as it is. port.sh case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac The content of dockerlogin.sh #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker login username $docker-pass: docker login user password $docker-server: docker private address View pipeline View k8s service Guess you wants Goctl Installation k8s docker jenkins jenkins pipeline nginx etcd Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"log-collection.html":{"url":"log-collection.html","title":"Log Collection","keywords":"","body":"Log Collection [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In order to ensure the stable operation of the business and predict the unhealthy risks of the service, the collection of logs can help us observe the current health of the service. In traditional business development, when there are not many machine deployments, we usually log in directly to the server to view and debug logs. However, as the business increases, services continue to be split. The maintenance cost of the service will also become more and more complicated. In a distributed system, there are more server machines, and the service is distributed on different servers. When problems are encountered, We can't use traditional methods to log in to the server for log investigation and debugging. The complexity can be imagined. [!TIP] If it is a simple single service system, or the service is too small, it is not recommended to use it directly, otherwise it will be counterproductive. Prepare kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash Filebeat $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # Turn on json parsing json.keys_under_root: true json.add_error_key: true # Log file path paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # Define kafka topic field fields: log_topic: log-collection # Export to kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx is the path where filebeat.yaml is located go-stash configuration Create a new config.yaml file Add configuration content $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC Start services (start in order) Start kafka Start elasticsearch Start kibana Start go-stash Start filebeat Start the order-api service and its dependent services (order-api service in the go-zero-demo project) Visit kibana Enter 127.0.0.1:5601 [!TIP] Here we only demonstrate the logs generated by logx in the collection service, and log collection in nginx is the same. Reference kafka elasticsearch kibana filebeat go-stash filebeat Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"trace.html":{"url":"trace.html","title":"Trace","keywords":"","body":"Trace [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Foreword In the microservice architecture, the call chain may be very long, from http to rpc, and from rpc to http. Developers want to know the call status and performance of each link, the best solution is full link tracking. The tracking method is to generate its own spanID at the beginning of a request, and pass it down along the entire request link. We use this spanID to view the status of the entire link and performance issues. Let's take a look at the link implementation of go-zero. Code structure spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 Concept SpanContext Before introducing span, first introduce context. SpanContext saves the context information of distributed tracing, including Trace id, Span id and other content that needs to be passed downstream. The implementation of OpenTracing needs to pass the SpanContext through a certain protocol to associate the Span in different processes to the same Trace. For HTTP requests, SpanContext is generally passed using HTTP headers. Below is the spanContext implemented by go-zero by default type spanContext struct { traceId string // TraceID represents the globally unique ID of tracer spanId string // SpanId indicates the unique ID of a span in a single trace, which is unique in the trace } At the same time, developers can also implement the interface methods provided by SpanContext to realize their own contextual information transfer: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // Custom operation TraceId, SpanId } Span A REST call or database operation, etc., can be used as a span. span is the smallest tracking unit of distributed tracing. A trace is composed of multiple spans. The tracking information includes the following information: type Span struct { ctx spanContext serviceName string operationName string startTime time.Time flag string children int } Judging from the definition structure of span: In microservices, this is a complete sub-calling process, with the start of the call startTime, the context structure spanContext that marks its own unique attribute, and the number of child nodes of fork. Example application In go-zero, http and rpc have been integrated as built-in middleware. We use http, rpc, take a look at how tracing is used: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } Set header -> carrier to get the traceId and other information in the header Open a new span and encapsulate \"traceId, spanId\" in the context Obtain traceId and spanId from the aforementioned carrier \"that is, header\" -See if it is set in the header -If it is not set, it will be randomly generated and returned Generate a new ctx from request, encapsulate the corresponding information in ctx, and return From the above context, copy a copy to the current request In this way, the information of the span is passed to the downstream service along with the request. RPC There are client, server in rpc, so from tracing there are also clientTracing, serverTracing. The logic of serveTracing is basically the same as that of http. Let’s take a look at how clientTracing is used? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** Add the data in the pair to ctx in the form of a map ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } Get the span context information brought down by the upstream Create a new ctx from the acquired span, span \"inherit the traceId of the parent span\" Add the span generated data to ctx, pass it to the next middleware, and flow downstream Summary go-zero obtains the link traceID by intercepting the request, and then assigns a root Span at the entry of the middleware function, and then splits the child Spans in subsequent operations. Each span has its own specific identification. After Finsh Will be collected in the link tracking system. Developers can trace the traceID through the ELK tool to see the entire call chain. At the same time, go-zero does not provide a complete set of trace link solutions. Developers can encapsulate the existing span structure of go-zero, build their own reporting system, and access links such as jaeger, zipkin, etc. Tracking tool. Reference go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-monitor.html":{"url":"service-monitor.html","title":"Monitor","keywords":"","body":"Monitor [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In microservice governance, service monitoring is also a very important link. Monitoring whether a service is working normally needs to be carried out from multiple dimensions, such as:* mysql indicators mongo indicators redis indicator Request log Service index statistics Service health check ... The monitoring work is very large, and this section only uses the service indicator monitoring as an example for illustration. Microservice indicator monitoring based on prometheus After the service is online, we often need to monitor the service so that we can find the problem early and make targeted optimization. The monitoring can be divided into various forms, such as log monitoring, call chain monitoring, indicator monitoring, and so on. Through indicator monitoring, the changing trend of service indicators can be clearly observed, and the operating status of the service can be understood, which plays a very important role in ensuring the stability of the service. Prometheus is an open source system monitoring and warning tool that supports a powerful query language, PromQL, allowing users to select and aggregate time series data in real time. Time series data is actively pulled by the server through the HTTP protocol, or it can be pushed through an intermediate gateway. Data, you can obtain monitoring targets through static configuration files or service discovery Prometheus architecture The overall architecture and ecosystem components of Prometheus are shown in the following figure: Prometheus Server pulls monitoring indicators directly from the monitoring target or indirectly through the push gateway. It stores all captured sample data locally and executes a series of rules on this data to summarize and record new time series or existing data. Generate an alert. The monitoring data can be visualized through Grafana or other tools go-zero service indicator monitoring based on prometheus The go-zero framework integrates prometheus-based service indicator monitoring. Below we use go-zero’s official example short url to demonstrate how to collect and monitor service indicators: The first step is to install Prometheus first, please refer to the official documentation for the installation steps go-zero does not enable prometheus monitoring by default. The opening method is very simple. You only need to add the following configuration to the shorturl-api.yaml file, where Host is the Prometheus Server address, which is a required configuration, the Port port is not filled in and the default is 9091, and the Path is used The path to pull metrics is /metrics by default Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics Edit the prometheus configuration file prometheus.yml, add the following configuration, and create targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json Edit the targets.json file, where targets is the target address configured by shorturl, and add several default tags [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] Start the prometheus service, listening on port 9090 by default $ prometheus --config.file=prometheus.yml Enter http://127.0.0.1:9090/ in the browser, and then click Status -> Targets to see the job whose status is Up, and the default label we configured can be seen in the Labels column Through the above steps, we have completed the configuration work of Prometheus for the indicator monitoring collection of the shorturl service. For the sake of simplicity, we have performed manual configuration. In the actual production environment, we generally use the method of regularly updating configuration files or service discovery to configure monitoring. Goals, space is limited, not explained here, interested students please check the relevant documents on their own Types of indicators monitored by go-zero go-zero currently adds monitoring of request metrics to the http middleware and rpc interceptor. Mainly from the two dimensions of request time and request error. The request time uses the Histogram metric type to define multiple Buckets to facilitate quantile statistics. The request error uses the Counter type, and the path tag rpc metric is added to the http metric. Added the method tag for detailed monitoring. Next, demonstrate how to view monitoring indicators: First execute the following command multiple times on the command line $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" Open Prometheus and switch to the Graph interface, and enter the {path=\"/shorten\"} command in the input box to view the monitoring indicators, as shown below: We use PromQL grammar query to filter the indicators whose path is /shorten, and the results show the indicator name and indicator value. The code value in the http_server_requests_code_total indicator is the status code of http, 200 indicates that the request is successful, and http_server_requests_duration_ms_bucket separately counts the results of different buckets. , You can also see that all the indicators have added the default indicators we configured The Console interface mainly displays the index results of the query. The Graph interface provides us with a simple graphical display interface. In the actual production environment, we generally use Grafana for graphical display. grafana dashboard Grafana is a visualization tool with powerful functions and supports multiple data sources such as Prometheus, Elasticsearch, Graphite, etc. For simple installation, please refer to the official documentation. The default port of grafana is 3000. After installation, enter http://localhost:3000/ in the browser. , The default account and password are both admin. The following demonstrates how to draw the visual interface based on the above indicators: Click on the left sidebar Configuration->Data Source->Add data source to add a data source, where the HTTP URL is the address of the data source Click on the left sidebar to add dashboard, and then add Variables to facilitate filtering for different tags, such as adding app variables to filter different services Enter the dashboard and click Add panel in the upper right corner to add a panel to count the qps of the interface in the path dimension The final effect is shown below. Different services can be filtered by service name. The panel shows the trend of qps with path /shorten. Summary The above demonstrates the simple process of go-zero based on prometheus+grafana service indicator monitoring. In the production environment, different dimensions of monitoring and analysis can be done according to the actual scenario. Now go-zero's monitoring indicators are mainly for http and rpc, which is obviously insufficient for the overall monitoring of the service, such as the monitoring of container resources, the monitoring of dependent mysql, redis and other resources, and the monitoring of custom indicators, etc. Go-zero will continue to optimize in this regard. Hope this article can help you Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl is a code generation tool under the go-zero microservice framework. Using goctl can significantly improve development efficiency and allow developers to focus their time on business development. Its functions include: api service generation rpc service generation model code generation template management This section will contain the following: Auto Completion Commands & Flags API Commands RPC Commands Model Commands Plugin Commands More Commands goctl? Many people will pronounce goctl as go-C-T-L. This is a wrong way of thinking. You should refer to go control and pronounce ɡō kənˈtrōl. View version information $ goctl -v If goctl is installed, it will output text information in the following format: goctl version ${version} ${os}/${arch} For example output: goctl version 1.1.5 darwin/amd64 Version number description version: goctl version number os: Current operating system name arch: Current system architecture name Install goctl The way one(go get) $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl Use this command to install the goctl tool into the GOPATHbin directory The way two (fork and build) Pull a source code from the go-zero code repository git@github.com:zeromicro/go-zero.git, enter the tools/goctl directory to compile the goctl file, and then add it to the environment variable. After the installation is complete, execute goctl -v. If the version information is output, the installation is successful, for example: $ goctl -v goctl version 1.1.4 darwin/amd64 FAQ command not found: goctl Please make sure that goctl has been installed, or whether goctl has been correctly added to the environment variables of the current shell. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"Auto Completion","keywords":"","body":"goctl autocomplete goctl autocomplete only supports unix-like operating systems Usage $ goctl completion -h Name. goctl autocomplete - generates completion scripts, it only works on unix-like operating systems Usage. goctl completion [command options] [arguments...] (command options). Options. --name value, -n value filename of the autocomplete script, default is [goctl_autocomplete] Generate autocomplete file $ goctl completion Generate autocomplete successfully! Execute the following script to set up the shell. echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell configuration zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc Demo effect Use the tab key to bring up the autocomplete prompt $ goctl api -- generate api-related files bug -- report a bug completion -- generates a completion script, which is only available for unix-like operating systems docker -- generates a Docker file help h -- displays a list of commands or help information for a command kube -- generates kubernetes files migrate -- migrate from tal-tech to zeromicro model -- Generate model code rpc -- generate rpc code template -- Template operations upgrade -- upgrade goctl to the latest version Translated with www.DeepL.com/Translator (free version) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"Commands & Flags","keywords":"","body":"goctl command list [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl bug (report a bug) upgrade (upgrade goctl to latest version) env (check or edit goctl environment) --write, -w: edit goctl environment check (detect goctl env and dependency tools) --force, -f: silent installation of non-existent dependencies --install, -i: install dependencies if not found migrate (migrate from tal-tech to zeromicro) --verbose, -v: verbose enables extra logging --version: the target release version of github.com/zeromicro/go-zero to migrate api (generate api related files) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure -o: the output api file new (fast create api service) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (format api files) --declare: use to skip check api types already declare --dir: the format target dir --iu: ignore update --stdin: use stdin to input api doc content, press \"ctrl + d\" to send EOF validate (validate api file) --api: validate target api file doc (generate doc files) --dir: the target dir -o: the output markdown directory go (generate go files for provided api in yaml file) --api: the api file --branch: the branch of the remote repo, it does work with --remote --dir: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (generate java files for provided api in api file) --api: the api file --dir: the target dir ts (generate ts files for provided api in api file) --api: the api file --caller: the web api caller --dir: the target dir --unwrap: unwrap the webapi caller for import --webapi: the web api file path dart (generate dart files for provided api in api file) --api: the api file --dir: the target dir --hostname: hostname of the server --legacy: legacy generator for flutter v1 kt (generate kotlin code for provided api file) --api: the api file --dir: the target directory --pkg: define package name for kotlin file plugin (custom file generator) --api: the api file --dir: the target directory --plugin, -p: the plugin file --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (generate Dockerfile) --branch: the branch of the remote repo, it does work with --remote --go: the file that contains main function --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --port: the port to expose, default none (default: 0) --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --scratch: use scratch for the base docker image --tz: the timezone of the container (default: Asia/Shanghai) --version: the goctl builder golang image version kube (generate kubernetes files) deploy (generate deployment yaml file) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --image: the docker image of deployment --limitCpu: the limit cpu to deploy (default: 1000) --limitMem: the limit memory to deploy (default: 1024) --maxReplicas: the max replicas of deploy (default: 10) --minReplicas: the min replicas to deploy (default: 3) --name: the name of deployment --namespace: the namespace of deployment --nodePort: the nodePort of the deployment to expose (default: 0) --port: the port of the deployment to listen on pod (default: 0) --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --replicas: the number of replicas to deploy (default: 3) --requestCpu: the request cpu to deploy (default: 500) --requestMem: the request memory to deploy (default: 512) --revisions: the number of revision history to limit (default: 5) --secret: the secret to image pull from registry --serviceAccount: the ServiceAccount for the deployment -o: the output yaml file rpc (generate rpc code) new (generate rpc demo service) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: whether the command execution environment is from idea plugin. [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (generate proto template) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --out, -o: the target path of proto --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure protoc (generate grpc code) --branch: the branch of the remote repo, it does work with --remote --home: the goctl home path of the template --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out: the zrpc output directory proto (generate rpc from proto) --branch: the branch of the remote repo, it does work with --remote --dir, -d: the target path of the code --go_opt: native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional] --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: whether the command execution environment is from idea plugin. [optional] --proto_path, -I: native command of protoc, specify the directory in which to search for imports. [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --src, -s: the file path of the proto source file --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] model (generate model code) mysql (generate mysql model) ddl (generate mysql model from ddl) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --database, --db: the name of database [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --src, -s: the path or path globbing patterns of the ddl --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (generate model from datasource) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: the table or table globbing patterns in the database --url: the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" pg (generate postgresql model) datasource (generate model from datasource) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --idea: for idea plugin [optional] --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --schema, -s: the table schema, default is [public] --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: the table or table globbing patterns in the database --url: the data source of database,like \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (generate mongo model) --branch: the branch of the remote repo, it does work with --remote --cache, -c: generate code with cache [optional] --dir, -d: the target dir --home: the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote: the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --style: the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t: specified model type name template (template operation) init (initialize the all templates(force update)) --home: the goctl home path of the template clean (clean the all cache templates) --home: the goctl home path of the template update (update template of the target category to the latest) --category, -c: the category of template, enum [api,rpc,model,docker,kube] --home: the goctl home path of the template revert (revert the target template to the latest) --category, -c: the category of template, enum [api,rpc,model,docker,kube] --home: the goctl home path of the template --name, -n: the target file name of template completion (generation completion script, it only works for unix-like OS) --name, -n: the filename of auto complete script, default is [goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-api.html":{"url":"goctl-api.html","title":"API Commands","keywords":"","body":"API Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl api is one of the core modules in goctl. It can quickly generate an api service through the .api file with one click. If you just start a go-zero api demo project, You can complete an api service development and normal operation without even coding. In traditional api projects, we have to create directories at all levels, write structures, Define routing, add logic files, this series of operations, if calculated according to the business requirements of a protocol, it takes about 5 to 6 minutes for the entire coding to actually enter the writing of business logic. This does not consider the various errors that may occur during the writing process. With the increase of services and the increase of agreements, the time for this part of the preparation work will increase proportionally. The goctl api can completely replace you to do this part of the work, no matter how many agreements you have, in the end, it only takes less than 10 seconds to complete. [!TIP] The structure is written, and the route definition is replaced by api, so in general, it saves you the time of creating folders, adding various files and resource dependencies. API command description $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help As you can see from the above, according to the different functions, the api contains a lot of self-commands and flags, let’s focus on it here The go subcommand, which function is to generate golang api services, let's take a look at the usage help through goctl api go -h: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir: Code output directory --api: Specify the api source file --style: Specify the file name style of the generated code file, see for details https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md Usage example $ goctl api go -api user.api -dir . -style gozero Guess you wants API IDL API Directory Structure Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"RPC Commands","keywords":"","body":"RPC Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Goctl Rpc is an rpc service code generation module under goctl scaffolding, supporting proto template generation and rpc service code generation, through this tool to generate code you only need to focus on business logic writing instead of writing some repetitive code. This allows us to focus on the business, thus speeding up the development efficiency and reducing the error rate of the code. Features Easy to use Fast and efficient development Low error rate Close to protoc Quick start Way 1: Quickly generate greet services Generated by the command goctl rpc new ${servieName} If you generate the greet rpc service. $ goctl rpc new greet The code structure after execution is as follows: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] See rpc directory for details of the new version of the directory. Way 2: Generate rpc service by specifying proto 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\". /user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } Generate rpc service code $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. Preparing Installed the go environment protoc & protoc-gen-go are installed, and environment variables have been set For more questions, see Notes Usage rpc service generation usage goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=. /pb --go-grpc_out=. /pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority, the git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote Parameters description --zrpc_out optional, default is the directory where the proto file is located, the target directory of the generated code --style optional, the naming style of the output directory, see https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md for details --home optional, specify the template path --remote Optional, specify the template remote repository. --branch Optional, specify the template remote repository branch, use with --remote You can understand that zrpc code generation is done with goctl rpc $protoc_command --zrpc_out=${output} templates, as in the original command to generate grpc code $ protoc user.proto --go_out=. --go-grpc_out=. then the zrpc code command would be $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out and --go-grpc_out must generate the same final directory The final directories generated by --go_out & --go-grpc_out and --zrpc_out must not be the same directory, otherwise pb.go and _grpc.pb.go are at the same level as the main function, which is not allowed. The directories produced by --go_out and --go-grpc_out are affected by the go_package values in the --go_opt and --grpc-go_opt and proto source files. To understand the generation logic here, it is recommended to read the Official documentation: Go Generated Code What developers need to do Focus on business code writing, leave the repetitive, non-business related work to goctl, after generating the rpc service code, developers only need to modify write configuration files in the service (etc/xx.json, internal/config/config.go) Business logic writing in the service (internal/logic/xxlogic.go) Writing of resource contexts in services (internal/svc/servicecontext.go) Caution proto does not support simultaneous generation of multiple files at the moment proto does not support external dependency package introduction, message does not support inline Currently main file, shared file, handler file will be forced to overwrite, and developers need to write manually will not overwrite the generation, this kind of code in the header are // Code generated by goctl. do not EDIT! // Source: xxx.proto Please be careful not to write business code in it; and do not write it inside business code. proto import For requestType and returnType in rpc must be defined in main proto file, for message in proto you can import other proto files like protoc. proto example: errorimport syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request and return do not support import } Correct import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// support import } message Response { base.Out out = 2;// support import } service Greet { rpc Ping(Request) returns(Response); } Guess what you want to see rpc directory rpc-config rpc-call Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-model.html":{"url":"goctl-model.html","title":"Model Commands","keywords":"","body":"Model Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl model is one of the components in the tool module under go-zero. It currently supports the recognition of mysql ddl for model layer code generation. It can be selectively generated with or without redis cache through the command line or idea plug-in (supported soon) The code logic. Quick start Generated by ddl $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c CURD code can be quickly generated after executing the above command. model ├── usermodel.go ├── usermodel_gen.go └── vars.go Generated by datasource $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } Usage $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help Generation rules Default rule By default, users will create createTime and updateTime fields (ignoring case and underscore naming style) when creating a table, and the default values are both CURRENT_TIMESTAMP, and updateTime supports ON UPDATE CURRENT_TIMESTAMP. For these two fields, insert, It will be removed when update is not in the assignment scope. Of course, if you don't need these two fields, it does not matter. ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote Generate code only basic CURD structure. Cache For the cache, I chose to list it in the form of question and answer. I think this can more clearly describe the function of the cache in the model. What information will the cache? For the primary key field cache, the entire structure information will be cached, while for the single index field (except full-text index), the primary key field value will be cached. Does the data update (update) operation clear the cache? Yes, but only clear the information in the primary key cache, why? I won't go into details here. Why not generate updateByXxx and deleteByXxx codes based on single index fields? There is no problem in theory, but we believe that the data operations of the model layer are based on the entire structure, including queries. I do not recommend querying only certain fields (no objection), otherwise our cache will be meaningless. Why not support the code generation layer of findPageLimit and findAll? At present, I think that in addition to the basic CURD, the other codes are all business-type codes. I think it is better for developers to write according to business needs. Type conversion rules mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"Plugin Commands","keywords":"","body":"Plugin Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Goctl supports custom plugins for api, so how do I customize a plugin? Let's take a look at an example of how to finally use it below. $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . The above command can be broken down into the following steps: goctl parsing api file goctl passes the parsed structure ApiSpec and parameters to the goctl-android executable file goctl-android customizes the generation logic according to the ApiSpec structure. The first part of this command goctl api plugin -p is a fixed parameter, goctl-android=\"android -package com.tal\" is a plugin parameter, where goctl-android is the plugin binary file, and android -package com.tal is a custom parameter of the plugin , -Api user.api -dir. Is a common custom parameter for goctl. How to write a custom plug-in? A very simple custom plug-in demo is included in the go-zero framework. The code is as follows: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() The function of this line of code is to parse the data passed from goctl, which contains the following parts: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api: defines the structure data of the api file Style: optional, it is used to control file naming conventions Dir: workDir Complete android plugin demo project based on plugin https://github.com/zeromicro/goctl-android Guess you wants API Directory Structure API IDL API Configuration API Commands Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-other.html":{"url":"goctl-other.html","title":"More Commands","keywords":"","body":"More Commands [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR goctl docker goctl kube goctl docker goctl docker can quickly generate a Dockerfile to help developers/operations and maintenance personnel speed up the deployment pace and reduce deployment complexity. Prepare docker install Dockerfile note Choose the simplest mirror: For example, alpine, the entire mirror is about 5M Set mirror time zoneRUN apk add --no-cache tzdata ENV TZ Asia/Shanghai Multi-stage build Otherwise, an executable file will be built in the first stage of construction to ensure that the build process is independent of the host The second stage uses the output of the first stage as input to construct the final minimalist image Dockerfile writing process First install the goctl tool $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl Create a hello service under the greet project $ goctl api new hello The file structure is as follows: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go Generate a Dockerfile in the hello directory$ goctl docker -go hello.go Dockerfile: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] To build mirror in the greet directory $ docker build -t hello:v1 -f service/hello/Dockerfile . View mirror hello v1 5455f2eaea6b 7 minutes ago 18.1MB It can be seen that the mirror size is about 18M. Start service$ docker run --rm -it -p 8888:8888 hello:v1 Test service$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker summary The goctl tool greatly simplifies the writing of Dockerfile files, provides best practices out of the box, and supports template customization. goctl kube goctl kube provides the function of quickly generating a k8s deployment file, which can speed up the deployment progress of developers/operations and maintenance personnel and reduce deployment complexity. Have a trouble to write K8S deployment files? K8S yaml has a lot of parameters, need to write and check? How to set the number of retained rollback versions? How to detect startup success, how to detect live? How to allocate and limit resources? How to set the time zone? Otherwise, the print log is GMT standard time How to expose services for other services to call? How to configure horizontal scaling based on CPU and memory usage? First, you need to know that you have these knowledge points, and secondly, it is not easy to understand all these knowledge points, and again, it is still easy to make mistakes every time you write! Create service image For demonstration, here we take the redis:6-alpine image as an example. 完整 K8S Deployment file writing process First install the goctl tool $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl One-click generation of K8S deployment files $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 The generated yaml file is as follows: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 Deploy the service, if the adhoc namespace does not exist, please create it through kubectl create namespace adhoc $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created View service permission status $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s Test service$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube summary The goctl tool greatly simplifies the writing of K8S yaml files, provides best practices out of the box, and supports template customization. Guess you wants Prepare API Directory Structure API IDL API Configuration API Commands Docker K8s Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-manage.html":{"url":"template-manage.html","title":"Template","keywords":"","body":"Template Command Custom Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-cmd.html":{"url":"template-cmd.html","title":"Command","keywords":"","body":"Template Operation Template is the basis of data-driven generation, all code (rest api, rpc, model, docker, kube) generation will rely on template. By default, the template generator selects the in-memory template for generation, while for developers who need to modify the template, they need to drop the template and make template changes in the next code generation. For developers who need to modify the templates, they need to modify the templates, and then the next time the code is generated, it will load the templates under the specified path to generate. Help NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help Init NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template Clean NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template Update NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template Revert NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home Specify the template storage path Template loading You can specify the folder where the template is located by --home during code generation, and the commands that have been supported to specify the template directory are goctl api go Details can be found in goctl api go --help for help goctl docker Details can be viewed with goctl docker --help goctl kube Details can be viewed with goctl kube --help goctl rpc new Details can be viewed with goctl rpc new --help goctl rpc proto Details can be viewed with goctl rpc proto --help goctl model mysql ddl Details can be viewed with goctl model mysql ddl --help goctl model mysql datasource Details can be viewed with goctl model mysql datasource --help goctl model postgresql datasource Details can be viewed with goctl model mysql datasource --help goctl model mongo Details can be viewed with goctl model mongo --help The default (when --home is not specified) is to read from the $HOME/.goctl directory. Example Initialize the template to the specified $HOME/template directory$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! Greet rpc generation using $HOME/template template$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template.html":{"url":"template.html","title":"Custom","keywords":"","body":"Template Modification Scenario Implement a uniformly formatted body accordingly, in the following format. { \"code\": 0, \"msg\": \"OK\", \"data\": {}// ① } ① 实际相应数据 [!TIP] The code generated by go-zero does not process it Preparation We go ahead and write a Response method in the response package under the project with module as greet, with a directory tree similar to the following. greet ├── response │ └── response.go └── xxx... The code is as follows package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } Modify the handler template $ vim ~/.goctl/api/handler.tpl Replace the template with the following package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① Replace with your real response package name, for reference only ② Customized template content [!TIP] 1.If there is no local ~/.goctl/api/handler.tpl file, you can initialize it with the template initialization command goctl template init Comparison Before func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // The following content will be replaced by custom templates if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } After func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } Comparison of response body Before { \"message\": \"Hello go-zero!\" } After { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } Summary This document only describes the process of customizing the template for the corresponding example of http, in addition to the following scenarios of customizing the template. model layer adds kmq model layer to generate the model instance of the option to be valid http customize the corresponding format ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"extended-reading.html":{"url":"extended-reading.html","title":"Extended","keywords":"","body":"Components [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The component center will include all components in the go-zero core folder, Therefore, it will be relatively large, and this resource will continue to be updated, and everyone is welcome to contribute to the document. This section will contain the following directories (in order of document update time): shorturl logx bloom executors fx mysql redis-lock periodlimit tokenlimit TimingWheel Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"logx.html":{"url":"logx.html","title":"logx","keywords":"","body":"logx [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Example var c logx.LogConf // Initialize the configuration from the yaml file conf.MustLoad(\"config.yaml\", &c) // logx is initialized according to the configuration logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() Initialization logx has many configurable items, you can refer to the definition in logx.LogConf. Currently available logx.MustSetUp(c) Perform the initial configuration. If the initial configuration is not performed, all the configurations will use the default configuration. Level The print log levels supported by logx are: info error server fatal slow stat You can use the corresponding method to print out the log of the corresponding level. At the same time, in order to facilitate debugging and online use, the log printing level can be dynamically adjusted. The level can be set through logx.SetLevel(uint32) or through configuration initialization. The currently supported parameters are: const ( // Print all levels of logs InfoLevel = iotas // Print errors, slows, stacks logs ErrorLevel // Only print server level logs SevereLevel ) Log mode At present, the log printing mode is mainly divided into two types, one is file output, and the other is console output. The recommended way, when using k8s, docker and other deployment methods, you can output the log to the console, use the log collector to collect and import it to es for log analysis. If it is a direct deployment method, the file output method can be used, and logx will automatically create log files corresponding to 5 corresponding levels in the specified file directory to save the logs. . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log At the same time, the file will be divided according to the natural day. When the specified number of days is exceeded, the log file will be automatically deleted, packaged and other operations. Disable log If you don't need log printing, you can use logx.Close() to close the log output. Note that when log output is disabled, it cannot be opened again. For details, please refer to the implementation of logx.RotateLogger and logx.DailyRotateRule. Close log Because logx uses asynchronous log output, if the log is not closed normally, some logs may be lost. The log output must be turned off where the program exits: logx.Close() Log configuration and shutdown related operations have already been done in most places such as rest and zrpc in the framework, so users don't need to care. At the same time, note that when the log output is turned off, the log cannot be printed again. Recommended writing: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration When we print the log, we may need to print the time-consuming situation, we can use logx.WithDuration(time.Duration), refer to the following example: startTime := timex.Now() // Database query rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } Will output the following format: {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} In this way, it is easy to collect statistics about slow sql related information. TraceLog tracingEntry is customized for link tracing log output. You can print the traceId and spanId information in the context. With our rest and zrpc, it is easy to complete the related printing of the link log. The example is as follows: logx.WithContext(context.Context).Info(\"This is info!\") SysLog Some applications may use system log for log printing. Logx uses the same encapsulation method, which makes it easy to collect log-related logs into logx. logx.CollectSysLog() Log configuration related LogConf Define the basic configuration required for the logging system The complete definition is as follows: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode defines the log printing method. The default mode is console, which will print to the console. The currently supported modes are as follows: console Print to the console file Print to access.log, error.log, stat.log and other files in the specified path volume In order to print to the storage that the mount comes in in k8s, because multiple pods may overwrite the same file, the volume mode automatically recognizes the pod and writes separate log files according to the pod. Path Path defines the output path of the file log, the default value is logs. Level Level defines the log printing level, and the default value is info. The currently supported levels are as follows: info error severe Compress Compress defines whether the log needs to be compressed, the default value is false. When Mode is file mode, the file will finally be packaged and compressed into a .gz file. KeepDays KeepDays defines the maximum number of days to keep logs. The default value is 0, which means that old logs will not be deleted. When Mode is file mode, if the maximum retention days are exceeded, the old log files will be deleted. StackCooldownMillis StackCooldownMillis defines the log output interval, the default is 100 milliseconds. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"bloom.html":{"url":"bloom.html","title":"bloom","keywords":"","body":"bloom [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The go-zero microservice framework provides many out-of-the-box tools. Good tools can not only improve the performance of the service, but also improve the robustness of the code to avoid errors, and realize the uniformity of the code style for others to read, etc. A series of articles will respectively introduce the use of tools in the go-zero framework and their implementation principles. Bloom filter bloom When doing server development, I believe you have heard of Bloom filters, you can judge whether a certain element is in the collection, because there are certain misjudgments and delete complex problems, the general usage scenario is: to prevent cache breakdown (to prevent malicious Attacks), spam filtering, cache digests, model detectors, etc., to determine whether there is a row of data to reduce disk access and improve service access performance. The simple cache package bloom.bloom provided by go-zero, the simple way to use it is as follows. // Initialize redisBitSet store := redis.NewRedis(\"redis 地址\", redis.NodeType) // Declare a bitSet, key=\"test_key\" name and bits are 1024 bits bitSet := newRedisBitSet(store, \"test_key\", 1024) // Determine whether the 0th bit exists isSetBefore, err := bitSet.check([]uint{0}) // Set the 512th bit to 1 err = bitSet.set([]uint{512}) // Expires in 3600 seconds err = bitSet.expire(3600) // Delete the bitSet err = bitSet.del() Bloom briefly introduced the use of the most basic redis bitset. The following is the real bloom implementation. Position the element hash // The element is hashed 14 times (const maps=14), and byte (0-13) is appended to the element each time, and then the hash is performed. // Take the modulo of locations[0-13], and finally return to locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i Add elements to bloom // We can find that the add method uses the set methods of getLocations and bitSet. // We hash the elements into uint slices of length 14, and then perform the set operation and store them in the bitSet of redis. func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } Check if there is an element in bloom // We can find that the Exists method uses the check method of getLocations and bitSet // We hash the elements into uint slices of length 14, and then perform bitSet check verification, return true if it exists, false if it does not exist or if the check fails func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } This section mainly introduces the core.bloom tool in the go-zero framework, which is very practical in actual projects. Good use of tools is very helpful to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In go-zero, executors act as a task pool, do multi-task buffering, and use tasks for batch processing. Such as: clickhouse large batch insert, sql batch insert. At the same time, you can also see executors in go-queue [In queue, ChunkExecutor is used to limit the byte size of task submission]. So when you have the following requirements, you can use this component: Submit tasks in batches Buffer part of tasks and submit lazily Delay task submission Before explaining it in detail, let's give a rough overview: Interface design Under the executors package, there are the following executors: Name Margin value bulkexecutor Reach maxTasks [Maximum number of tasks] Submit chunkexecutor Reach maxChunkSize[Maximum number of bytes] Submit periodicalexecutor basic executor delayexecutor Delay the execution of the passed fn() lessexecutor You will see that except for the special functions of delay and less, the other three are all combinations of executor + container: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // Option mode: It appears in many places in go-zero. In multiple configurations, better design ideas // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute the function that actually does the execution] [maxTasks execution critical point] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. It can be seen that the underlying bulkexecutor depends on the periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } And this container is an interface: TaskContainer interface { // Add task to container AddTask(task interface{}) bool // Is actually to execute the incoming execute func() Execute(tasks interface{}) // When the critical value is reached, remove all tasks in the container and pass them to execute func() through the channel for execution RemoveAll() interface{} } This shows the dependency between: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] So if you want to complete your own executor, you can implement these three interfaces of container, and then combine with periodicalexecutor. So back to the picture 👆, our focus is on the periodicalexecutor, and see how it is designed? How to use First look at how to use this component in business: There is a timed service to perform data synchronization from mysql to clickhouse at a fixed time every day: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } Initialize bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() is the real insert execution function [requires developers to write specific business logic by themselves] dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // The container will automatically refresh the task to execute every 3s. executors.WithBulkTasks(10240), // The maximum number of tasks for the container. Generally set to a power of 2 ) } [!TIP] An additional introduction: clickhouse is suitable for mass insertion, because the insert speed is very fast, mass insert can make full use of clickhouse Main business logic preparation: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] You may be wondering why Flush(), Wait() is needed, and I will analyze it through the source code later. There are 3 steps to use as a whole: Add(): Add to task Flush(): Refresh tasks in container Wait(): Wait for the completion of all tasks Source code analysis [!TIP] The main analysis here is periodicalexecutor, because the other two commonly used executors rely on it. Initialization func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander: Pass the channel of tasks container: Temporarily store the task of Add() confirmChan: Block Add(), at the beginning of this time, executeTasks() will let go of blocking ticker: To prevent the blocking of Add(), there will be a chance to execute regularly and release the temporarily stored task in time Add() After initialization, the first step in the business logic is to add task to executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask will pop and return tasks in the container if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } In addAndCheck(), AddTask() is controlling the maximum number of tasks. If it exceeds the number of tasks, RemoveAll() will be executed, and the tasks pop of the temporarily stored container will be passed to the commander, followed by goroutine loop reading , And then execute tasks. backgroundFlush() Start a background coroutine, and constantly refresh the tasks in the container: func (pe *PeriodicalExecutor) backgroundFlush() { // Encapsulate go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // Get []tasks from channel case vals := pe.interval*idleRound { // If maxTask is not reached, Flush() err, and last->now is too long, Flush() will be triggered again // Only when this is reversed will a new backgroundFlush() background coroutine be opened pe.guarded = false // Refresh again to prevent missing pe.Flush() return } } } }) } Overall two processes: commander receives the tasks passed by RemoveAll(), then executes it, and releases the blocking of Add() to continue Add() It’s time for ticker, if the first step is not executed, it will automatically Flush() and execute the task. Wait() In backgroundFlush(), a function is mentioned: enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } By enumerating in this way, you can know why you have to bring dts.insertExecutor.Wait() at the end. Of course, you have to wait for all goroutine tasks to complete. Thinking In looking at the source code, I thought about some other design ideas, do you have similar questions: In the analysis of executors, you will find that there are lock in many places [!TIP] There is a race condition in go test, use locking to avoid this situation After analyzing confirmChan, it was found that this submit only appeared, why is it designed like this? It used to be: wg.Add(1) was written in executeTasks(); now it is: first wg.Add(1), then release confirmChan blocking If the execution of executor func is blocked, Add task is still in progress, because there is no block, it may be executed to Executor.Wait() soon, and this is where wg.Wait() appears in wg.Add () before execution, this will be panic For details, please see the latest version of TestPeriodicalExecutor_WaitFast(), you may wish to run on this version to reproduce. Summary There are still a few more analysis of executors, I leave it to you to look at the source code. In short, the overall design: Follow interface-oriented design Flexible use of concurrent tools such as channel and waitgroup The combination of execution unit + storage unit There are many useful component tools in go-zero. Good use of tools is very helpful to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"fx.html":{"url":"fx.html","title":"fx","keywords":"","body":"fx [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR fx is a complete stream processing component. It is similar to MapReduce, fx also has a concurrent processing function: Parallel(fn, options). But at the same time it is not only concurrent processing. From(chan), Map(fn), Filter(fn), Reduce(fn), etc., read from the data source into a stream, process the stream data, and finally aggregate the stream data. Is it a bit like Java Lambda? If you were a Java developer before, you can understand the basic design when you see this. Overall API Let's get an overview of how fx is constructed as a whole: The marked part is the most important part of the entire fx: From APIs such as From(fn), a data stream Stream is generated A collection of APIs for converting, aggregating, and evaluating Stream So list the currently supported Stream API: API Function Distinct(fn) Select a specific item type in fn and de-duplicate it Filter(fn, option) fn specifies specific rules, and the element that meets the rules is passed to the next stream Group(fn) According to fn, the elements in stream are divided into different groups Head(num) Take out the first num elements in stream and generate a new stream Map(fn, option) Convert each ele to another corresponding ele and pass it to the next stream Merge() Combine all ele into one slice and generate a new stream Reverse() Reverse the element in stream. [Use double pointer] Sort(fn) Sort elements in stream according to fn Tail(num) Take out the last num elements of stream to generate a new stream. [Using a doubly linked list] Walk(fn, option) Apply fn to every element of source. Generate a new stream No longer generates a new stream, do the final evaluation operation: API Function ForAll(fn) Process stream according to fn, and no longer generate stream [evaluation operation] ForEach(fn) Perform fn [evaluation operation] on all elements in stream Parallel(fn, option) Concurrently apply the given fn and the given number of workers to each element[evaluation operation] Reduce(fn) Directly process stream [evaluation operation] Done() Do nothing, wait for all operations to complete How to use? result := make(map[string]string) fx.From(func(source chan From() generates stream from a slice Walk() receives and a stream, transforms and reorganizes each ele in the stream to generate a new stream Finally, the stream output (fmt.Println), storage (map,slice), and persistence (db operation) are performed by the evaluation operation Briefly analyze The function naming in fx is semantically. Developers only need to know what kind of conversion is required for the business logic and call the matching function. So here is a brief analysis of a few more typical functions. Walk() Walk() is implemented as the bottom layer by multiple functions throughout fx, such as Map(), Filter(), etc. So the essence is: Walk() is responsible for concurrently applying the passed function to each ele of the input stream and generating a new stream. Following the source code, it is divided into two sub-functions: custom count by worker, default count is worker // Custom workers func (p Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream { pipe := make(chan interface{}, option.workers) go func() { var wg sync.WaitGroup // channel Use buffered channel as a concurrent queue to limit the number of concurrent waitgroup to ensure the completeness of the task completion Another walkUnlimited(): also uses waitgroup for concurrency control, because there is no custom concurrency limit, so there is no other channel for concurrency control. Tail() The introduction of this is mainly because the ring is a doubly linked list, and the simple algorithm is still very interesting. func (p Stream) Tail(n int64) Stream { source := make(chan interface{}) go func() { ring := collection.NewRing(int(n)) // Sequence insertion, the order of the source is consistent with the order of the ring for item := range p.source { ring.Add(item) } // Take out all the items in the ring for _, item := range ring.Take() { source As for why Tail() can take out the last n of the source, this is left for everyone to fine-tune. Here is my understanding: [!TIP] Suppose there is the following scenario,Tail(5) stream size :7 ring size:5 Here you can use the method of pulling apart the ring-shaped linked list, Loop-to-line,At this point, divide the symmetry axis by the full length, flip the extra elements, and the following elements are the parts needed by Tail(5). [!TIP] The graph is used here for a clearer performance, but everyone should also look at the code. Algorithm to be tested Stream Transform Design Analyzing the entire fx, you will find that the overall design follows a design template: func (p Stream) Transform(fn func(item interface{}) interface{}) Stream { // make channel source := make(chan interface{}) // goroutine worker go func() { // transform for item := range p.source { ... source stream return Range(source) } channel as a container for streams Open goroutine to convert source, aggregate, and send to channel Processed,close(outputStream) channel -> stream Summary This concludes the basic introduction of fx. If you are interested in other API source code, you can follow the API list above to read one by one. At the same time, it is also recommended that you take a look at the API of java stream, and you can have a deeper understanding of this stream call. At the same time, there are many useful component tools in go-zero. Good use of tools will greatly help improve service performance and development efficiency. I hope this article can bring you some gains. Reference go-zero Java Stream Stream API in Java 8 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mysql.html":{"url":"mysql.html","title":"mysql","keywords":"","body":"Mysql [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR go-zero provides easier operation of mysql API. [!TIP] But stores/mysql positioning is not an orm framework. If you need to generate model layer code through sql/scheme -> model/struct reverse engineering, developers can use goctl model, this is an excellent feature. Features Provides a more developer-friendly API compared to native Complete the automatic assignment of queryField -> struct Insert \"bulkinserter\" in batches Comes with fuse API has been continuously tested by several services Provide partial assignment feature, do not force strict assignment of struct Connection Let's use an example to briefly explain how to create a mysql connected model: // 1. Quickly connect to a mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. Call in the `servicecontext`, understand the logic layer call of the model upper layer model.NewMysqlModel(heraMysql, tablename), // 3. model layer mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. Create a batch insert [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD Prepare an User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` UserName string `db:\"user_name\"` Sex int `db:\"sex\"` MobilePhone string `db:\"mobile_phone\"` } Among them, userBuilderQueryRows: go-zero provides struct -> [field...] conversion. Developers can use this as a template directly. insert // An actual insert model layer operation func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } Splicing insertsql Pass in insertsql and the struct field corresponding to the placeholder -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...): args... needs to correspond to the placeholder in sql. Otherwise, there will be problems with assignment exceptions. go-zero unified and abstracted operations involving mysql modification as Exec(). So the insert/update/delete operations are essentially the same. For the remaining two operations, the developer can try the above insert process. query You only need to pass in the querysql and model structure, and you can get the assigned model. No need for developers to manually assign values. func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } Declare model struct, splicing querysql conn.QueryRow(&model, querysql, args...): args... corresponds to the placeholder in querysql. [!WARNING] The first parameter in QueryRow() needs to be passed in Ptr \"The bottom layer needs to be reflected to assign a value to struct\" The above is to query one record, if you need to query multiple records, you can use conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } The difference from QueryRow() is: model needs to be set to Slice, because it is to query multiple rows, and multiple models need to be assigned. But at the same time you need to pay attention to ️: the first parameter needs to be passed in Ptr querypartial In terms of use, it is no different from the above-mentioned QueryRow(), \"this reflects the highly abstract design of go-zero.\" the difference: QueryRow(): len(querysql fields) == len(struct), and one-to-one correspondence QueryRowPartial() :len(querysql fields) numA: Number of database fields; numB: the number of defined struct attributes. If numA , but you just need to unify multiple queries, \"multiple struct is defined to return different purposes, and all of them can use the same querysql\", you can use QueryRowPartial() Transaction To perform a series of operations in a transaction, the general process is as follows: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // Any error returned will roll back the transaction if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // You can also continue to perform insert/update/delete related operations return nil }) As in the above example, developers only need to wrap all operations in transaction in a function func(session sqlx.Session) error {}, if the operation in the transaction returns any error, Transact( ) will automatically roll back the transaction. Distributed transactions go-zero has deeply cooperated with dtm and has natively supported distributed transactions, see distributed-transaction for details Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis-lock","keywords":"","body":"redis lock [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Since it is a lock, the first function that comes to mind is: Anti-repeated clicks, only one request has an effect at a time. Since it is redis, it must be exclusive and also have some common features of locks: High performance No deadlock No lock failure after the node is down In go-zero, redis set key nx can be used to ensure that the write is successful when the key does not exist. px can automatically delete the key after the timeout. \"The worst case is that the key is automatically deleted after the timeout, so that there will be no death. lock\" example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. Optional operation, set the redislock expiration time redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"another user is currently operating, please try again later\") } defer func() { recover() redisLock.Release() }() It is the same as when you use sync.Mutex. Lock and unlock, perform your business operations. Acquire the lock lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } First introduce several redis command options, the following are the added options for the set command: ex seconds : Set the key expiration time, in s px milliseconds : set the key expiration time in milliseconds nx : When the key does not exist, set the value of the key xx : When the key exists, the value of the key will be set The input parameters involved in lua script: args example description KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD Unique ID: random string ARGV[2] 30000 Set the expiration time of the lock Then talk about the code features: The Lua script guarantees atomicity \"Of course, multiple operations are implemented as one operation in Redis, that is, a single command operation\" Use set key value px milliseconds nx value is unique When locking, first determine whether the value of the key is consistent with the previous setting, and modify the expiration time if it is consistent Release lock delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } You only need to pay attention to one point when releasing the lock: Can't release other people's locks, can't release other people's locks, can't release other people's locks Therefore, you need to first get(key) == value「key」, and then go to delete if it is true Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit","keywords":"","body":"periodlimit [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Whether in a single service or in a microservice, the API interface provided by the developer for the front end has an upper limit of access. When the frequency of access or the amount of concurrency exceeds its tolerance, we must consider current limit to ensure the interface. Availability or degraded availability. That is, the interface also needs to be installed with a fuse to prevent the system from being paralyzed due to excessive pressure on the system by unexpected requests. This article will introduce periodlimit. Usage const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero adopts a sliding window counting method to calculate the number of accesses to the same resource within a period of time. If it exceeds the specified limit, access is denied. Of course, if you are accessing different resources within a period of time, the amount of access to each resource does not exceed the limit. In this case, a large number of requests are allowed to come in. In a distributed system, there are multiple microservices to provide services. So when instantaneous traffic accesses the same resource at the same time, how to make the counter count normally in the distributed system? At the same time, when computing resources are accessed, multiple calculations may be involved. How to ensure the atomicity of calculations? go-zero counts resource visits with the help of incrby of redis Use lua script to do the whole window calculation to ensure the atomicity of calculation Let's take a look at several key attributes controlled by lua script: argument mean key[1] Logo for access to resources ARGV[1] limit => the total number of requests, if it exceeds the rate limit. Can be set to QPS ARGV[2] window size => sliding window, use ttl to simulate the effect of sliding -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- If it is the first visit, set the expiration time => TTL = window size -- Because it only limits the number of visits for a period if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current As for the above return code, return it to the caller. The caller decides to request subsequent operations: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit The following picture describes the process of request entry and the subsequent situation when the request triggers limit: Subsequent processing If a large batch of requests comes in at a certain point in the service, the periodlimit reaches the limit threshold in a short period of time, and the set time range is far from reaching. The processing of subsequent requests becomes a problem. It is not processed in periodlimit, but code is returned. The processing of subsequent requests is left to the developer. If it is not processed, it is simply to reject the request If these requests need to be processed, developers can use mq to buffer the requests to ease the pressure of the requests Use tokenlimit to allow temporary traffic impact So in the next article, we will talk about tokenlimit Summary The periodlimit current limiting scheme in go-zero is based on redis counters. By calling redis lua script, it guarantees the atomicity of the counting process and guarantees that the counting is normal under distributed conditions. However, this scheme has disadvantages because it needs to record all behavior records within the time window. If this amount is particularly large, memory consumption will become very serious. Reference go-zero periodlimit Distributed service current limit actual combat, has already lined up the pits for you tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"tokenlimit","keywords":"","body":"tokenlimit [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This section will introduce its basic usage through token limit (tokenlimit). Usage const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit On the whole, the token bucket production logic is as follows: The average sending rate configured by the user is r, then a token is added to the bucket every 1/r second; Assume that at most b tokens can be stored in the bucket. If the token bucket is full when the token arrives, then the token will be discarded; When the traffic enters at the rate v, the token is taken from the bucket at the rate v, the traffic that gets the token passes, and the traffic that does not get the token does not pass, and the fuse logic is executed; go-zero adopts the method of lua script under both types of current limiters, relying on redis to achieve distributed current limiting, and lua script can also achieve atomicity of token production and read operations. Let's take a look at several key attributes controlled by lua script: argument mean ARGV[1] rate 「How many tokens are generated per second」 ARGV[2] burst 「Maximum token bucket」 ARGV[3] now_time「Current timestamp」 ARGV[4] get token nums 「The number of tokens that the developer needs to obtain」 KEYS[1] Tokenkey representing the resource KEYS[2] The key that represents the refresh time -- Return whether the expected token can be obtained alive local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:How long does it take to fill the token_bucket local fill_time = capacity/rate -- Round down the fill time local ttl = math.floor(fill_time*2) -- Get the number of remaining tokens in the current token_bucket -- If it is the first time to enter, set the number of token_bucket to the maximum value of the token bucket local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- The time when the token_bucket was last updated local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- Calculate the number of new tokens based on the span between the current time and the last update time, and the rate of token production -- If it exceeds max_burst, excess tokens produced will be discarded local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- Update the new token number and update time redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed It can be seen from the above that the lua script: only involves the operation of the token, ensuring that the token is produced and read reasonably. Function analysis Seen from the above flow: There are multiple guarantee mechanisms to ensure that the current limit will be completed. If the redis limiter fails, at least in the process rate limiter will cover it. Retry the redis limiter mechanism to ensure that it runs as normally as possible. Summary The tokenlimit current limiting scheme in go-zero is suitable for instantaneous traffic shocks, and the actual request scenario is not at a constant rate. The token bucket is quite pre-request, and when the real request arrives, it won't be destroyed instantly. When the traffic hits a certain level, consumption will be carried out at a predetermined rate. However, in the production of token, dynamic adjustment cannot be made according to the current flow situation, and it is not flexible enough, and further optimization can be carried out. In addition, you can refer to Token bucket WIKI which mentioned hierarchical token buckets, which are divided into different queues according to different traffic bandwidths. Reference go-zero tokenlimit Redis Rate Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"TimingWheel","keywords":"","body":"TimingWheel [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR This article introduces the delayed operation in go-zero. Delayed operation, two options can be used: Timer: The timer maintains a priority queue, executes it at the time, and then stores the tasks that need to be executed in the map The timingWheel in collection maintains an array for storing task groups, and each slot maintains a doubly linked list of tasks. When the execution starts, the timer executes the tasks in a slot every specified time. Scheme 2 reduces the maintenance task from the priority queue O(nlog(n)) to the doubly linked list O(1), and the execution of the task only needs to poll the tasks O(N) at a time point. Priority queue, put and delete elements O(nlog(n)). timingWheel in cache First, let's first talk about the use of TimingWheel in the cache of collection: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel This is the initialization of cache and the initialization of timingWheel at the same time for key expiration processing. The parameters in turn represent: interval: Time division scale numSlots: time slots execute: execute a function at a point in time The function executed in the cache is to delete the expired key, and this expiration is controlled by the timingWheel to advance the time. Next, let's learn about it through the use of timingWheel by cache. Initialization func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // Single time grid time interval ticker: ticker, // Timer, do time push, advance by interval slots: make([]*list.List, numSlots), // Time wheel timers: NewSafeMap(), // Store the map of task{key, value} [parameters needed to execute execute] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // Execution function numSlots: numSlots, // Initialize slots num setChannel: make(chan timingEntry), // The following channels are used for task delivery moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // Prepare all the lists stored in the slot tw.initSlots() // Open asynchronous coroutine, use channel for task communication and delivery go tw.run() return tw, nil } The above is a more intuitive display of the \"time wheel\" of the timingWheel, and the details of the advancement will be explained later around this picture. go tw.run() opens a coroutine for time promotion: func (tw *TimingWheel) run() { for { select { // Timer do time push -> scanAndRunTasks() case It can be seen that the timer execution is started at the time of initialization, and it is rotated in the internal time period, and then the bottom layer keeps getting the tasks from the list in the slot and handing them over to the execute for execution. Task Operation The next step is to set the cache key: func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } First look at whether this key exists in the data map If it exists, update expire -> MoveTimer() Set the key for the first time -> SetTimer() So the use of timingWheel is clear. Developers can add or update according to their needs. At the same time, when we follow the source code, we will find that: SetTimer() MoveTimer() all transports tasks to channel, and the coroutine opened in run() continuously takes out the task operations of channel. SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() From the above call chain, there is a function that will be called: moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => Get [positionEntry「pos, task」] by key val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay The delay time is less than a time grid interval, and there is no smaller scale, indicating that the task should be executed immediately if task.delay interval, the new pos, circle in the time wheel is calculated by the delay time delay pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // Move offset before and after recording. To re-enter the team for later process timer.item.diff = pos - timer.pos } else if circle > 0 { // Move to the next layer and convert circle to part of diff circle-- timer.item.circle = circle // Because it is an array, add numSlots [that is equivalent to going to the next level] timer.item.diff = tw.numSlots + pos - timer.pos } else { // If the offset is advanced, the task is still in the first layer at this time // Mark to delete the old task, and re-enter the team, waiting to be executed timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } The above process has the following situations: delay : Because For changed delay: new >= old: newCircle> 0: Calculate diff and convert circle to the next layer, so diff + numslots If only the delay time is shortened, delete the old task mark, re-add it to the list, and wait for the next loop to be executed Execute In the previous initialization, the timer in run() kept advancing, and the process of advancing was mainly to pass the tasks in the list to the executed execute func. Let's start with the execution of the timer: // Timer \"It will be executed every internal\" func (tw *TimingWheel) onTick() { // Update the current execution tick position every time it is executed tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // Get the doubly linked list of stored tasks in the tick position at this time l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } Next is how to execute execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // Store the task{key, value} that needs to be executed at present [parameters required by execute, which are passed to execute in turn] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // Mark the deletion, do the real deletion in scan \"Delete the map data\" if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // The current execution point has expired, but it is not at the first level at the same time, so now that the current level has been completed, it will drop to the next level // But did not modify pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // Because the diff has been marked before, you need to enter the queue again next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // The above cases are all cases that cannot be executed, and those that can be executed will be added to tasks tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks, and then execute each task->execute tw.runTasks(tasks) } The specific branching situation is explained in the comments. When you look at it, it can be combined with the previous moveTask(), where the circle drops, and the calculation of diff is the key to linking the two functions. As for the calculation of diff, the calculation of pos, circle is involved: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } The above process can be simplified to the following: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots Summary The timingWheel is driven by the timer. As the time advances, the tasks of the list \"doubly linked list\" in the current time grid will be taken out and passed to the execute for execution. In terms of time separation, the time wheel has circle layers, so that the original numSlots can be reused continuously, because the timer is constantly loop, and execution can drop the upper layer slot to the lower layer. You can execute the task up to the upper level continuously in the loop. There are many useful component tools in go-zero. Good use of tools is of great help to improve service performance and development efficiency. I hope this article can bring you some gains. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"eco.html":{"url":"eco.html","title":"Around","keywords":"","body":"go-zero around Tools Intellij Plugin VSCode Plugin Distributed Transaction Plugins Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tool-center.html":{"url":"tool-center.html","title":"Tools","keywords":"","body":"Tools [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In go-zero, a lot of tools to improve engineering efficiency are provided, such as api and rpc generation. On this basis, the compilation of api files seems so weak. Because of the lack of highlighting, code hints, template generation, etc., this section will show you how go-zero solves these problems. This section contains the following subsections: Intellij plugin VSCode plugin Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"intellij.html":{"url":"intellij.html","title":"Intellij Plugin","keywords":"","body":"Intellij Plugin [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Go-Zero Plugin Introduction A plug-in tool that supports go-zero api language structure syntax highlighting, detection, and quick generation of api, rpc, and model. Idea version requirements IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ Features api syntax highlighting api syntax and semantic detection struct, route, handler repeated definition detection type jump to the type declaration position Support api, rpc, mode related menu options in the context menu Code formatting (option+command+L) Code hint Install The way one Find the latest zip package in the GitHub release, download it and install it locally. (No need to unzip) Thw way two In the plugin store, search for Goctl to install Preview Create a new Api(Proto) file In the project area target folder right click ->New-> New Api(Proto) File ->Empty File/Api(Proto) Template, as shown in the figure: Quickly generate api/rpc service In the target folder right click->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code generation The way one(Project Panel) Corresponding files (api, proto, sql) right click->New->Go Zero-> Api/Rpc/Model Code, as shown in the figure: Thw way two(Editor Panel) Corresponding files (api, proto, sql) right click -> Generate-> Api/Rpc/Model Code Error message Live Template Live Template can speed up our writing of api files. For example, when we enter the main keyword in the go file, press the tip and press Enter and insert a piece of template code. func main(){ } In other words, you will be more familiar with the picture below. Once upon a time, you still defined the template here. Let’s enter the instructions for using the template in today’s api grammar. Let’s take a look at the effect of the service template. First of all, in the previous picture, take a look at several template effective areas in the api file (psiTree element area) Default template and effective scope keyword psiTree effective scope description @doc ApiService doc comment template doc ApiService doc comment template struct Struct struct declaration template info ApiFile info block template type ApiFile type group template handler ApiService handler name template get ApiService get method routing template head ApiService head method routing template post ApiService post method routing template put ApiService put method routing template delete ApiService delete method routing template connect ApiService connect method routing template options ApiService options method routing template trace ApiService trace method routing template service ApiFile service service block template json Tag、Tag literal tag template xml Tag、Tag literal tag template path Tag、Tag literal tag template form Tag、Tag literal tag template For the corresponding content of each template, you can view the detailed template content in Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags, for example, the json tag template content is json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"vscode.html":{"url":"vscode.html","title":"VSCode Plugin","keywords":"","body":"vs code plugin [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The plug-in can be installed on the 1.46.0+ version of Visual Studio Code. First, please make sure that your Visual Studio Code version meets the requirements and the goctl command line tool has been installed. If Visual Studio Code is not installed, please install and open Visual Studio Code. Navigate to the \"Extensions\" pane, search for goctl and install this extension (publisher ID is \"xiaoxin-technology.goctl\"). For the extension of Visual Studio Code, please refer to here. Features Syntax highlighting Jump to definition/reference Code formatting Code block hint Syntax highlighting Jump to definition/reference Code formatting Invoke the goctl command line formatting tool, please make sure that goctl has been added to $PATH and has executable permissions before use Code block hint info block type block service block handler block Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"Distributed Transaction","keywords":"","body":"Distributed Transaction Application Scenario In a microservices architecture, when we need to ensure data consistency across services, the local database transactions are overwhelmed and cannot put multiple operations across databases and services into a single transaction. There are many such scenarios and we can list many of them. Order systems: need to ensure that order creation and stock deduction either both succeeded or both rolled back. Inter-bank transfer scenarios: the data is not in one database, but you need to ensure that the balance deduction and balance increase either both succeeded or both failed. Travel booking scenario: several tickets need to be booked in the third-party system, either all succeeded or all cancelled. For these scenarios that cannot be solved by local transactions, we need a distributed transaction solution to ensure consistency of data updates across services and databases. Solution go-zero and dtm have cooperated deeply to introduce an elegant solution that seamlessly accesses dtm in go-zero. The solution has the following features. dtm services can be directly registered to go-zero's registry through configuration go-zero is able to access the dtm server with a built-in target format dtm can recognise go-zero's target format and dynamically access services in go-zero For detailed access, see the dtm documentation: go-zero support More application scenarios dtm can address not only the above distributed transaction scenarios, but also many more scenarios related to data consistency, including Database and cache consistency: dtm's two-phase messages can guarantee atomicity for database update operations, and cache update/delete operations Flash-sales system: dtm ensures that the number of orders created in a flash-sales scenario is exactly the same as the number of stock deductions, without the need for subsequent manual calibration Multiple storage combinations: dtm already supports multiple storage such as database, Redis, Mongo, etc., which can be combined into one global transaction to ensure data consistency For more information on dtm's capabilities and presentation, see dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"plugin-center.html":{"url":"plugin-center.html","title":"Plugins","keywords":"","body":"Plugins [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The goctl api provides plugin commands to support the function extension of the api. When the functions in the goctl api do not satisfy your use, Or you need to extend goctl api function customization, then the plug-in function will be very suitable for developers to be self-sufficient, see for details goctl plugin Plugin resources goctl-go-compact Goctl's default route merges one file into one file goctl-swagger Generate swagger documents through api files goctl-php goctl-php is a plug-in based on goctl, used to generate php call end (server end) http server request code Guess you wants Plugin Commands API IDL Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"learning-resource.html":{"url":"learning-resource.html","title":"Learning Resources","keywords":"","body":"Learning Resources [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR The latest learning resource channel of go-zero will be updated from time to time. Currently, the channels included are: Wechat Night OpenTalk Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"wechat.html":{"url":"wechat.html","title":"Wechat","keywords":"","body":"Wechat [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Microservices actual combat is the official official account of go-zero, where the latest go-zero best practices will be released, synchronized go night reading, go open source, GopherChina, Tencent Cloud Developer Conference and other channels about the latest go-zero Technology and information. Name Author QrCode 微服务实战 kevwan Articles(Simplified Chinese) Here are some articles. If you want to get more go-zero best practice dry goods, you can follow the public account for the latest developments. 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goreading.html":{"url":"goreading.html","title":"Night","keywords":"","body":"Night [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR 2020-08-16 XiaoHeiBan go-zero microservice framework architecture design 2020-10-03 go-zero microservice framework and online communication In-process shared calls to prevent cache breakdown Implement JWT authentication based on go-zero Goodbye go-micro! Enterprise project migration go-zero strategy (1) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gotalk.html":{"url":"gotalk.html","title":"OpenTalk","keywords":"","body":"OpenTalk [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR OpenTalk 4th - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-cache.html":{"url":"redis-cache.html","title":"Persistent layer cache","keywords":"","body":"Persistent layer cache [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Principles of Cache Design We only delete the cache without updating it. Once the data in the DB is modified, we will directly delete the corresponding cache instead of updating it. Let's see how the order of deleting the cache is correct. Delete the cache first, then update the DB Let's look at the situation of two concurrent requests. A request needs to update the data. The cache is deleted first, and then B requests to read the data. At this time, there is no data in the cache, and the data is loaded from the DB and written back to the cache, and then A updates the DB , Then the data in the cache will always be dirty data at this time, until the cache expires or there is a new data update request. As shown Update the DB first, then delete the cache A requests to update the DB first, and then B requests to read the data. At this time, the old data is returned. At this time, it can be considered that the A request has not been updated, and the final consistency is acceptable. Then A deletes the cache, and subsequent requests will Get the latest data, as shown in the figure Let's take another look at the normal request flow: The first request to update the DB and delete the cache The second request to read the cache, if there is no data, read the data from the DB and write it back to the cache All subsequent read requests can be read directly from the cache Let's take a look at the DB query, assuming that there are seven columns of ABCDEFG data in the row record: A request to query only part of the column data, such as ABC, CDE or EFG in the request, as shown in the figure Query a single complete row record, as shown in the figure Query part or all of the columns of multiple rows, as shown in the figure For the above three cases, firstly, we don’t need partial queries, because some queries cannot be cached. Once cached, the data is updated, and it is impossible to locate which data needs to be deleted; secondly, for multi-line queries, according to actual scenarios and If necessary, we will establish the corresponding mapping from the query conditions to the primary key in the business layer; and for the query of a single row of complete records, go-zero has a built-in complete cache management method. So the core principle is: go-zero cache must be a complete line record. Let's introduce in detail the cache processing methods of the three built-in scenarios in go-zero: Cache based on primary keyPRIMARY KEY (`id`) This kind of cache is relatively the easiest to handle, just use the primary key as the key in redis to cache line records. Cache based on unique index When doing index-based cache design, I used the design method of database index for reference. In database design, if you use the index to check data, the engine will first find the primary key in the tree of index -> primary key, and then use the primary key. To query row records, an indirect layer is introduced to solve the corresponding problem of index to row records. The same principle applies to the cache design of go-zero. Index-based cache is divided into single-column unique index and multi-column unique index: But for go-zero, single-column and multi-column are just different ways of generating cache keys, and the control logic behind them is the same. Then go-zero's built-in cache management can better control the data consistency problem, and also built-in to prevent the breakdown, penetration, and avalanche problems of the cache (these were discussed carefully when sharing at the gopherchina conference, see follow-up gopherchina Share video). In addition, go-zero has built-in cache access and access hit rate statistics, as shown below: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 But for go-zero, single-column and multi-column are just different ways of generating cache keys, and the control logic behind them is the same. Then go-zero's built-in cache management can better control the data consistency problem, and also built-in to prevent the breakdown, penetration, and avalanche problems of the cache (these were discussed carefully when sharing at the gopherchina conference, see follow-up gopherchina Share video). The single-column unique index is as follows: UNIQUE KEY `product_idx` (`product`) The multi-column unique index is as follows: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) Cache code interpretation 1. Cache logic based on the primary key The specific implementation code is as follows: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } The Take method here is to first get the data from the cache via the key, if you get it, return it directly, if you can't get it, then use the query method to go to the DB to read the complete row record and write it back Cache, and then return the data. The whole logic is relatively simple and easy to understand. Let's take a look at the implementation of Take in detail: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } The logic of Take is as follows: Use key to find data from cache If found, return the data If you can't find it, use the query method to read the data After reading it, call c.SetCache(key, v) to set the cache The code and explanation of doTake are as follows: // v - The data object that needs to be read // key - Cache key // query - Method used to read complete data from DB // cacheVal - Method used to write cache func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // Use barriers to prevent cache breakdown and ensure that there is only one request in a process to load the data corresponding to the key val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // Read data from the cache if err := c.doGetCache(key, v); err != nil { // If it is a placeholder that was put in beforehand (to prevent cache penetration), then the default errNotFound is returned // If it is an unknown error, then return directly, because we can't give up the cache error and directly send all requests to the DB, // This will kill the DB in a high concurrency scenario if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // request DB // If the returned error is errNotFound, then we need to set a placeholder in the cache to prevent the cache from penetrating if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // Statistics DB failed c.stat.IncrementDbFails() return nil, err } // Write data to cache if err = cacheVal(v); err != nil { logx.Error(err) } } // Return json serialized data return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // Write data to the incoming v object return jsonx.Unmarshal(val.([]byte), v) } 2. Cache logic based on unique index Because this block is more complicated, I used different colors to mark out the code block and logic of the response. block 2 is actually the same as the cache based on the primary key. Here, I mainly talk about the logic of block 1. The block 1 part of the code block is divided into two cases: The primary key can be found from the cache through the index. At this time, the primary key is used directly to walk the logic of block 2, and the follow-up is the same as the above-based primary key-based caching logic. The primary key cannot be found in the cache through the index Query the complete row record from the DB through the index, if there is an error, return After the complete row record is found, the cache of the primary key to the complete row record and the cache of the index to the primary key will be written to redis at the same time Return the required row data // v-the data object that needs to be read // key-cache key generated by index // keyer-Use the primary key to generate a key based on the primary key cache // indexQuery-method to read complete data from DB using index, need to return the primary key // primaryQuery-method to get complete data from DB with primary key func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // First query the cache through the index to see if there is a cache from the index to the primary key if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // If there is no cache of the index to the primary key, then the complete data is queried through the index primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // The complete data is queried through the index, set to “found” and used directly later, no need to read data from the cache anymore found = true // Save the mapping from the primary key to the complete data in the cache. The TakeWithExpire method has saved the mapping from the index to the primary key in the cache. return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // The data has been found through the index, just return directly if found { return nil } // Read data from the cache through the primary key, if the cache is not available, read from the DB through the primaryQuery method and write back to the cache and then return the data return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } Let's look at a practical example func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // Generate index-based keys indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // Generate a complete data cache key based on the primary key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // Index-based DB query method func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // DB query method based on primary key func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // Error handling, you need to determine whether the returned sqlc.ErrNotFound is, if it is, we use the ErrNotFound defined in this package to return // Prevent users from perceiving whether or not the cache is used, and at the same time isolate the underlying dependencies switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } All the above cache automatic management codes can be automatically generated through goctl, and the internal CRUD and cache of our team are basically automatically generated through goctl, which can save A lot of development time, and the cache code itself is also very error-prone. Even with good code experience, it is difficult to write it correctly every time. Therefore, we recommend using automatic cache code generation tools as much as possible to avoid errors. Guess you wants The fourth phase-how to design go-zero cache in OpenTalk Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"Business layer cache","keywords":"","body":"Business layer cache [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the previous article Persistent Layer Cache, the db layer cache was introduced. In retrospect, the main design of the db layer cache can be summarized as follows: The cache is only deleted but not updated Only one row record is always stored, that is, the row record corresponding to the primary key The unique index only caches the primary key value, not the row record directly (refer to the mysql index idea) Anti-cache penetration design, one minute by default Do not cache multi-line records Preface In a large-scale business system, by adding a cache to the persistence layer, for most single-line record queries, it is believed that the cache can help the persistence layer reduce a lot of access pressure, but in actual business, data reading is not just a single-line record. In the face of many multi-line records, this will also cause a lot of access pressure on the persistence layer. In addition, it is unrealistic to rely solely on the persistence layer for high-concurrency scenarios such as the spike system and the course selection system. In this section, we introduce the cache design in go-zero practice-biz cache. Examples of applicable scenarios subject system Content social system Spike... Like these systems, we can add another layer of cache to the business layer to store key information in the system, such as the student selection information in the course selection system, the remaining number of courses in the course selection system, and the content information during a certain period of time in the content social system. Next, let's take an example of a content social system. In the content social system, we generally query a batch of content lists first, and then click on a piece of content to view the details. Before adding biz cache, the query flowchart of content information should be: From the figure and the previous article [Persistence Layer Cache] (redis-cache.md), we can know that there is no way to get the content list to rely on the cache. If we add a layer of cache to the business layer to store the key information (or even the complete information) in the list, then access to multiple rows of records is no longer a problem, and this is what biz redis will do. Next, let’s take a look at the design plan, assuming that a single-line record in the content system contains the following fields. Field Name Field Type Remarks id string Content id title string Title content string Content createTime time.Time Create time Our goal is to obtain a batch of content lists, and try to avoid the access pressure caused by the content list going to the db. First, we use the sort set data structure of redis to store. The amount of field information that needs to be stored is based on two redis storage schemes: Cache local information The key field information (such as id, etc.) is compressed and stored according to certain rules. For score, we use the createTime millisecond value (the time value is equal, not discussed here). The advantage of this storage scheme is to save redis storage space. On the other hand, the disadvantage is that the detailed content of the list needs to be checked back again (but this back check will use the row record cache of the persistence layer) Cache complete information All published content will be stored after being compressed according to certain rules. For the same score, we still use the createTime millisecond value. The advantage of this storage solution is that business additions, deletions, checks, and changes are all redis, while the db layer is at this time. You don’t need to consider the row record cache. The persistence layer only provides data backup and recovery. On the other hand, its shortcomings are also obvious. The storage space and configuration requirements are higher, and the cost will increase. Sample code type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent provides content storage func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent provides content deletion func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // Content compression func compress(c *Content) string { // todo: do it yourself var ret string return ret } // Content decompression func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime provides data query based on time period func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } In the above example, redis does not set an expiration time. We will synchronize the add, delete, modify, and check operations to redis. We think that the content social system has a relatively high list access request to do this scheme design. In addition, there are also some data visits. I did not expect the content design system to visit so frequently. It may be a sudden increase in visits within a certain period of time, and then it may be visited again for a long time. At this interval, In other words, I will not visit again. In this scenario, how should I consider the design of the cache? In the practice of go-zero content, there are two solutions to this problem: Increased memory cache: The memory cache is used to store data that may have a large amount of sudden access. Commonly used storage schemes use map data structure to store. Map data storage is relatively simple to implement, but cache expiration processing needs to be increased The timer comes out, another solution is through Cache in the go-zero library, It is specialized Used for memory management. Use biz redis and set a reasonable expiration time Summary The above two scenarios can contain most of the multi-line record cache. For scenarios where the query volume of multi-line records is not large, there is no need to put biz redis directly in it. You can try to let db take care of it first, and developers can monitor according to the persistence layer. And service Biz needs to be introduced when monitoring to measure. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-queue.html":{"url":"go-queue.html","title":"Queue","keywords":"","body":"Queue [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR In the development of daily tasks, we will have many asynchronous, batch, timing, and delayed tasks to be processed. There is go-queue in go-zero. It is recommended to use go-queue for processing. Go-queue itself is also developed based on go-zero. There are two modes dq : Depends on beanstalkd, distributed, can be stored, delayed, timing settings, shutdown and restart can be re-executed, messages will not be lost, very simple to use, redis setnx is used in go-queue to ensure that each message is only consumed once, usage scenarios Mainly used for daily tasks. kq: Depends on Kafka, so I won’t introduce more about it, the famous Kafka, the usage scenario is mainly to do message queue We mainly talk about dq. The use of kq is also the same, but it depends on the bottom layer. If you haven't used beanstalkd, you can google it first. It's still very easy to use. etc/job.yaml : Configuration file Name: job Log: ServiceName: job Level: info # dq depends on Beanstalks, redis, Beanstalks configuration, redis configuration DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go: Parse dq corresponding etc/*.yaml configuration /** * @Description Configuration file * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : Responsible for multi-task registration /** * @Description Register job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) group.Start() } ProducerLogic: One of the job business logic /** * @Description Producer task * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i Another job business logic /** * @Description Consumer task * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description Configuration * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go startup file /** * @Description Startup file * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) group := service.NewServiceGroup() handler.RegisterJob(ctx,group) ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) for { s := Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"datacenter.html":{"url":"datacenter.html","title":"Middle Ground System","keywords":"","body":"How do I use go-zero to implement a Middle Ground System? [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Author: Jack Luo Original link:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] I recently discovered that a new star microservice framework has emerged in the golang community. It comes from a good future. Just looking at the name, it is very exciting. Before, I only played go-micro. In fact, I have not used it in the project. I think that microservices and grpc are very noble. They have not been in the project yet. I have really played them. I saw that the tools provided by the government are really easy to use. They only need to be defined, and the comfortable file structure is generated. I am concerned about business, and there was a voting activity recently, and in recent years, Middle Ground System have been quite popular, so I decided to give it a try. SourceCode: https://github.com/jackluo2012/datacenter Let's talk about the idea of Middle Ground System architecture first: The concept of Middle Ground System is probably to unify the apps one by one. I understand it this way anyway. Let’s talk about user service first. Now a company has a lot of official accounts, small programs, WeChat, Alipay, and xxx xxx, and a lot of platforms. Every time we develop, we always need to provide user login services. Stop copying the code, and then we are thinking about whether we can have a set of independent user services, just tell me you need to send a platform you want to log in (such as WeChat), WeChat login, what is needed is that the client returns one to the server code, and then the server takes this code to WeChat to get user information. Anyway, everyone understands it. We decided to get all the information into the configuration public service, and then store the appid and appkey of WeChat, Alipay and other platforms, as well as the appid and appkey of payment, and write a set. Finally, let's talk about implementation, the whole is a repo: Gateway, we use: go-zero's Api service Others are services, we use go-zero rpc service Look at the directory structure After the whole project was completed, I worked alone and wrote about it for a week, and then I realized the above Middle Ground System. datacenter-api service Look at the official document first https://zeromicro.github.io/go-zero/ Let's set up the gateway first:: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter View the book directory: ➜ datacenter tree . └── go.mod 0 directories, 1 file Create api file ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api └── go.mod Define api service Respectively include the above Public Service, User Service, Voting Activity Service info( title: \"demo\" desc: \"demo\" author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) // Get application information type Beid struct { Beid int64 `json:\"beid\"` } type Token struct{ Token string `json:\"token\"` } type WxTicket struct{ Ticket string `json:\"ticket\"` } type Application struct { Sname string `json:\"Sname\"` Logo string `json:\"logo\"` Isclose int64 `json:\"isclose\"` Fullwebsite string `json:\"fullwebsite\"` } type SnsReq struct{ Beid Ptyid int64 `json:\"ptyid\"` // Platform ID BackUrl string `json:\"back_url\"` // Return address after login } type SnsResp struct{ Beid Ptyid int64 `json:\"ptyid\"` // Platform ID Appid string `json:\"appid\"` // sns Platform ID Title string `json:\"title\"` LoginUrl string `json:\"login_url\"` // WeChat login address } type WxShareResp struct { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"Get information about the site\" ) @handler votesVerification get /MP_verify_NT04cqknJe0em3mT.txt (SnsReq) returns (SnsResp) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"Get social attribute information of the site\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) // Get shared returns @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"Qiniu upload credentials\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } // Registration request type RegisterReq struct { Mobile string `json:\"mobile\"` Password string `json:\"password\"` Smscode string `json:\"smscode\"` } // Login request type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` // 1. Password login, 2. SMS login Password string `json:\"password\"` } // WeChat login type WxLoginReq struct { Beid int64 `json:\"beid\"` // Application id Code string `json:\"code\"` // WeChat AccesskKey Ptyid int64 `json:\"ptyid\"` // Platform ID } //Return user information type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` // Platform ID Ptyid int64 `json:\"ptyid\"` Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` Ptyid int64 `json:\"ptyid\"` } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service user-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service user-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } type Actid struct { Actid int64 `json:\"actid\"` } type VoteReq struct { Aeid int64 `json:\"aeid\"` Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` } type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` Descr string `json:\"descr\"` StartDate int64 `json:\"start_date\"` EnrollDate int64 `json:\"enroll_date\"` EndDate int64 `json:\"end_date\"` Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` Type int64 `json:\"type\"` Num int64 `json:\"num\"` } type EnrollReq struct { Actid Name string `json:\"name\"` Address string `json:\"address\"` Images []string `json:\"images\"` Descr string `json:\"descr\"` } type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` Name string `json:\"name\"` Address string `json:\"address\"` Images []string `json:\"images\"` Descr string `json:\"descr\"` Votecount int64 `json:\"votecount\"` Viewcount int64 `json:\"viewcount\"` } @server( group: votes ) service votes-api { @doc( summary: \"Get activity information\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"Activity visit +1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"Get information about registered voting works\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"Get a list of registered works\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service votes-api { @doc( summary: \"vote\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } The API and document ideas that are basically written above Generate datacenter api service ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter tree . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files We open etc/datacenter-api.yaml and add the necessary configuration information Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: pass Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc I will write the above UserRpc, CommonRpc, and VotesRpc first, and then add them by step. Let's write the CommonRpc service first. CommonRpc service New project directory ➜ datacenter mkdir -p common/rpc && cd common/rpc Just create it directly in the datacenter directory, because in common, it may not only provide rpc services in the future, but also api services, so the rpc directory is added goctl create template ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto Fill in the content: ➜ rpc cat common.proto syntax = \"proto3\"; package common; option go_package = \"common\"; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } message AppConfigReq { int64 beid=1; int64 ptyid=2; } message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl generates rpc service ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files Basically, all the catalog specifications and structure are generated, so there is no need to worry about the project catalog, how to put it, and how to organize it. Take a look at the configuration information, which can write mysql and other redis information: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc Let's add database services: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files So the basic rpc is finished, and then we connect the rpc with the model and the api. This official document is already very detailed, here is just the code: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } Modify in svc: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } The above code has already associated rpc with the model database, we will now associate rpc with api: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } Join the svc service: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"timeout %s: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } Basically, we can call it in the file directory of logic: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } In this way, it is basically connected, and basically there is no need to change the others. UserRPC and VotesRPC are similar, so I won't write them here. Reviews go-zero is really fragrant, because it has a goctl tool, which can automatically generate all the code structure, we will no longer worry about the directory structure, how to organize it, and there is no architectural ability for several years It’s not easy to implement. What are the norms, concurrency, circuit breaker, no use at all, test and filter other, concentrate on realizing the business, like microservices, but also service discovery, a series of things, don’t care, because go-zero has been implemented internally. I have written code for more than 10 years. The php I have been using before, the more famous ones are laravel and thinkphp, which are basically modular. Realizations like microservices are really costly, but you use go-zero. , You develop as simple as tune api interface, other service discovery, those do not need to pay attention at all, only need to pay attention to the business. A good language, framework, and their underlying thinking are always high-efficiency and no overtime thinking. I believe that go-zero will improve the efficiency of you and your team or company. The author of go-zero said that they have a team dedicated to organizing the go-zero framework, and the purpose should be obvious, that is, to improve their own development efficiency, process flow, and standardization, which are the criteria for improving work efficiency, as we usually encounter When it comes to a problem or encounter a bug, the first thing I think of is not how to solve my bug, but whether there is a problem with my process, which of my process will cause the bug, and finally I believe in go-zeroCan become the preferred framework for microservice development. Finally, talk about the pits encountered: grpc I used grpc for the first time, and then I encountered the problem that the field value is not displayed when some characters are empty: It is realized by jsonpb in the official library of grpc. The official setting has a structure to realize the conversion of protoc buffer to JSON structure, and can configure the conversion requirements according to the fields. Cross-domain issues It is set in go-zero, and it feels no effect. The big guy said that it was set through nginx, but later found that it still didn't work. Recently, I forcibly got a domain name, and I have time to solve it later. sqlx The sqlx problem of go-zero, this really took a long time: time.Time is a data structure. Timestamp is used in the database. For example, my field is delete_at. The default database setting is null. When the result is inserted, it reports Incorrect datetime value: '0000-00-00 'for column'deleted_at' at row 1\"} This error, the query time reported deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" I removed this field decisively and added the label .omitempty above the field, which seems to be useful too, db:\".omitempty\" The second is this Conversion from collation utf8_general_ci into utf8mb4_unicode_ci. The probable reason for this is that I like to use emj expressions now, and my mysql data cannot be recognized. data links mysql still follows the original way, modify the encoding format of the configuration file, re-create the database, and set the database encoding to utf8mb4, and the sorting rule is utf8mb4_unicode_ci. In this case, all tables and string fields are in this encoding format. If you don't want all of them, you can set them separately. This is not the point. Because it is easy to set on Navicat, just click manually。 Here comes the important point: Golang uses the github.com/go-sql-driver/mysql driver, which will connect to the dsn of mysql (because I am using gorm, dsn may be different from the native format. Too the same, but it’s okay, just pay attention to charset and collation) root:password@/name?parseTime=True&loc=Local&charset=utf8 is modified to: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"stream.html":{"url":"stream.html","title":"Stream Handler","keywords":"","body":"Stream processing [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Stream processing is a computer programming paradigm that allows given a data sequence (stream processing data source), a series of data operations (functions) are applied to each element in the stream. At the same time, stream processing tools can significantly improve programmers' development efficiency, allowing them to write effective, clean, and concise code. Streaming data processing is very common in our daily work. For example, we often record many business logs in business development. These logs are usually sent to Kafka first, and then written to elasticsearch by the Job consumption Kafka, and the logs are in progress. In the process of stream processing, logs are often processed, such as filtering invalid logs, doing some calculations and recombining logs, etc. The schematic diagram is as follows: fx go-zero is a full-featured microservice framework. There are many very useful tools built in the framework, including streaming data processing tools fx , let’s use a simple example to understand the tool: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) The inputStream function simulates the generation of stream data, and the outputStream function simulates the process of stream data. The From function is the input of the stream. The Walk function concurrently acts on each item. The Filter function filters the item as true and keeps it as false. Keep, the ForEach function traverses and outputs each item element. Intermediate operations of streaming data processing There may be many intermediate operations in the data processing of a stream, and each intermediate operation can act on the stream. Just like the workers on the assembly line, each worker will return to the processed new part after operating the part, and in the same way, after the intermediate operation of the flow processing is completed, it will also return to a new flow. Intermediate operations of fx stream processing: Operation function Features Input Distinct Remove duplicate items KeyFunc, return the key that needs to be deduplicated Filter Filter items that do not meet the conditions FilterFunc, Option controls the amount of concurrency Group Group items KeyFunc, group by key Head Take out the first n items and return to the new stream int64 reserved number Map Object conversion MapFunc, Option controls the amount of concurrency Merge Merge item into slice and generate new stream Reverse Reverse item Sort Sort items LessFunc implements sorting algorithm Tail Similar to the Head function, n items form a new stream after being taken out int64 reserved number Walk Act on each item WalkFunc, Option controls the amount of concurrency The following figure shows each step and the result of each step: Usage and principle analysis From Construct a stream through the From function and return the Stream, and the stream data is stored through the channel: // Example s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter The Filter function provides the function of filtering items, FilterFunc defines the filtering logic true to retain the item, and false to not retain: // Example: Keep even numbers s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group groups the stream data. The key of the group needs to be defined. After the data is grouped, it is stored in the channel as slices: // Example Group according to the first character \"g\" or \"p\", if not, it will be divided into another group ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse can reverse the elements in the stream: // Example fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // Source Code func (p Stream) Reverse() Stream { var items []interface{} // Get the data in the stream for item := range p.source { items = append(items, item) } // Reversal algorithm for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // Write stream return Just(items...) } Distinct Distinct de-duplicates elements in the stream. De-duplication is commonly used in business development. It is often necessary to de-duplicate user IDs, etc.: // Example fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // Output: 1,2,3,4,5,6 // Source Code func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // Deduplication is performed by key, and only one of the same key is kept keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // The key is not retained if it exists if _, ok := keys[key]; !ok { source Walk The concurrency of the Walk function works on each item in the stream. You can set the number of concurrency through WithWorkers. The default number of concurrency is 16, and the minimum number of concurrency is 1. If you set unlimitedWorkers to true, the number of concurrency is unlimited, but the number of concurrent writes in the stream is unlimited. The data is limited by defaultWorkers. In WalkFunc, users can customize the elements that are subsequently written to the stream, and can write multiple elements without writing: // Example fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan Concurrent processing In addition to stream data processing, the fx tool also provides function concurrency. The realization of a function in microservices often requires multiple services. Concurrent processing dependence can effectively reduce dependency time and improve service performance. fx.Parallel(func() { userRPC() }, func() { accountRPC() }, func() { orderRPC() }) Note that when fx.Parallel performs dependency parallel processing, there will be no error return. If you need an error return, or a dependency error report needs to end the dependency request immediately, please use the MapReduce tool To process. Summary This article introduces the basic concepts of stream processing and the stream processing tool fx in go-zero. There are many stream processing scenarios in actual production. I hope this article can give you some inspiration and better response Stream processing scene at work. Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"online-exchange.html":{"url":"online-exchange.html","title":"Online Exchange","keywords":"","body":"Summary of online communication issues on October 3,2020 [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Go-zero applicable scenarios I hope to talk about the application scenarios and the advantages of each scenario Highly concurrent microservice system Support tens of millions of daily activities, millions of QPS Complete microservice governance capabilities Support custom middleware Well managed database and cache Effectively isolate faults Monolithic system with low concurrency This kind of system can directly use the api layer without rpc service Use scenarios and use cases of each function Limiting Fuse Load reduction time out Observability The actual experience of go-zero The service is stable Front-end and back-end interface consistency, one api file can generate front-end and back-end code Less specification and less code means less bugs Eliminate api documents, greatly reducing communication costs The code structure is completely consistent, easy to maintain and take over Project structure of microservices, CICD processing of monorepo bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model The CI of the mono repo is made through gitlab, and the CD uses jenkins CI is as strict as possible, such as -race, using tools such as sonar CD has development, testing, pre-release, grayscale and formal clusters If it is in grayscale at 6 p.m. and there is no fault, it will automatically synchronize to the official cluster at 10 the next day The formal cluster is divided into multiple k8s clusters, which can effectively prevent a single cluster from failing, just remove it directly, and the cluster upgrade is better How to deploy and how to monitor? The full amount of K8S is automatically packaged into a docker image through jenkins, and the tag is packaged according to the time, so that you can see which day of the image is at a glance As mentioned above, pre-release -> grayscale -> formal Prometheus+ self-built dashboard service Detect service and request exceptions based on logs If you plan to change the go-zero framework to refactor your business, how can you make the online business stable and safe for users to switch without feeling? In addition, how to divide the service under consultation? Gradually replace, from outside to inside, add a proxy to proofread, you can switch after proofreading a week If there is a database reconstruction, you need to do a good job of synchronizing the old and the new Service division is based on business, following the principle of coarse to fine, avoiding one api and one microservice Data splitting is particularly important for microservices. The upper layer is easy to split, and the data is difficult to split. As far as possible, ensure that the data is split according to the business Service discovery Service discovery etcd key design Service key + timestamp, the probability of timestamp conflict in the number of service processes is extremely low, ignore it etcd service discovery and management, exception capture and exception handling Why k8s also uses etcd for service discovery, because the refresh of dns is delayed, resulting in a large number of failures in rolling updates, and etcd can achieve completely lossless updates The etcd cluster is directly deployed in the k8s cluster, because there are multiple formal clusters, clusters are single-pointed and registered to avoid confusion Automatically detect and refresh for etcd abnormalities or leader switching. When etcd has abnormalities that cannot be recovered, the service list will not be refreshed to ensure that the services are still available Cache design and use cases Distributed multiple redis clusters, dozens of largest online clusters provide caching services for the same service Seamless expansion and contraction There is no cache without expiration time to avoid a large amount of infrequently used data occupying resources, the default is one week Cache penetration, no data will be cached for one minute for a short period of time to avoid the system crashing due to interface brushing or a large number of non-existent data requests Cache breakdown, a process will only refresh the same data once, avoiding a large number of hot data being loaded at the same time Cache avalanche, automatically jitter the cache expiration time, with a standard deviation of 5%, so that the expiration time of a week is distributed within 16 hours, effectively preventing avalanches Our online database has a cache, otherwise it will not be able to support massive concurrency Automatic cache management has been built into go-zero, and code can be automatically generated through goctl Can you explain the design ideas of middleware and interceptor? Onion model This middleware processes, such as current limiting, fusing, etc., and then decides whether to call next next call Process the return result of the next call How to implement the transaction processing of microservices, the design and implementation of gozero distributed transactions, and what good middleware recommendations are there? 2PC, two-phase submission TCC, Try-Confirm-Cancel Message queue, maximum attempt Manual compensation How to design better multi-level goroutine exception capture? Microservice system request exceptions should be isolated, and a single exception request should not crash the entire process go-zero comes with RunSafe/GoSafe to prevent a single abnormal request from causing the process to crash Monitoring needs to keep up to prevent abnormal excess without knowing it The contradiction between fail fast and fault isolation Generation and use of k8s configuration (gateway, service, slb) K8s yaml file is automatically generated internally, which is too dependent on configuration and not open source I plan to add a k8s configuration template to the bookstore example slb->nginx->nodeport->api gateway->rpc service Gateway current limiting, fusing and load reduction There are two types of current limiting: concurrency control and distributed current limiting Concurrency control is used to prevent instantaneous excessive requests and protect the system from being overwhelmed Distributed current limit is used to configure different quotas for different services Fuse is to protect dependent services. When a service has a large number of exceptions, the caller should protect it so that it has a chance to return to normal and also achieve the effect of fail fast Downloading is to protect the current process from exhausting its resources and fall into complete unavailability, ensuring that the maximum amount of requests that can be carried is served as well as possible Load reduction and k8s can effectively protect k8s expansion, k8s expansion in minutes, go-zero load reduction in seconds Introduce useful components in core, such as timingwheel, etc., and talk about design ideas Bloom filter In-process cache RollingWindow TimingWheel Various executors fx package, map/reduce/filter/sort/group/distinct/head/tail... Consistent hash implementation Distributed current limiting implementation mapreduce, with cancel ability There are a lot of concurrency tools in the syncx package How to quickly add a kind of rpc protocol support, change the cross-machine discovery to the local node adjustment, and turn off the complex filter and load balancing functions go-zero has a relatively close relationship with grpc, and it did not consider supporting protocols other than grpc at the beginning of the design If you want to increase it, you can only fork out and change it. You can use the direct scheme directly by adjusting the machine Why remove filter and load balancing? If you want to go, fork is changed, but there is no need The design and implementation ideas of log and monitoring and link tracking, it is best to have a rough diagram Log and monitoring We use prometheus, customize the dashboard service, bundle and submit data (every minute) Link tracking can see the calling relationship and automatically record the trace log Is there any pooling technique useful for the go-zero framework? If so, in which core code can you refer to Generally do not need to optimize in advance, over-optimization is a taboo Core/syncx/pool.go defines a general pooling technology with expiration time Go-zero uses those performance test method frameworks, is there a code reference? You can talk about ideas and experience go benchmark Stress testing can be scaled up according to the estimated ratio by using existing business log samples The pressure test must be pressured until the system cannot be carried, see where the first bottleneck is, and then pressure again after the change, and cycle Talk about the abstract experience and experience of the code Don’t repeat yourself You may not need it. Before, business developers often asked me if I could add this function or that function. I usually ask the deep-level purpose carefully. In many cases, I find that this function is redundant, and it is the best practice to not need it. Martin Fowler proposed the principle of abstracting after three occurrences. Sometimes some colleagues will ask me to add a function to the framework. After I think about it, I often answer this. You write it in the business layer first. If there is a need in other places, you will tell me again, and it will appear three times. I will consider integrating into the framework A file should only do one thing as much as possible, each file should be controlled within 200 lines as much as possible, and a function should be controlled within 50 lines as much as possible, so that you can see the entire function without scrolling Need the ability to abstract and refine, think more, often look back and think about the previous architecture or implementation Will you publish a book on the go-zero framework from design to practice? What is the future development plan of the framework? There is no book publishing plan, and a good framework is the most important Continue to focus on engineering efficiency Improve service governance capabilities Help business development land as quickly as possible Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"contributor.html":{"url":"contributor.html","title":"Contributor","keywords":"","body":"Community contribution Author kevwan go-zero contributors kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy Suyghur heyanfu sjatsh Mikaelemmmm foliet wubenqi fondoger taobig shenbaise9527 reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl zzzfwww HarryWang29 workman-Lu wuqinqiang soasurs weicut almas1992 AlexLast appleboy pipi-lv Howie59 ShyunnY SnakeHacker sohamtembhurne toby1991 masonchen2014 lucaq lchjczw heyehang guonaihong wangzeping722 fyyang kscooo cuishuang bensonfx lovelly lowang-bh magickeha yangwenmai me-cs mlboy mongobaba moyrne ahmczsy lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang yiGmMk fffreedom linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 liyiwu zeromake zzhaolei zzZZzzz888 qwxingzhe sixwaaaay liuqing6767 peasfarmer lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle RivenChan toventang vankillua jsonMark shssen rcyw weibobo windk wojiukankan SeigeC wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 2822132073 l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen ch3nnn cuisongliu dahaihu dylanNew edieruby elza2 codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog Document contributors kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:51 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"Document Contribute","keywords":"","body":"Document Contribute [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR How to contribute documents? Click the \"Edit this page\" button at the top to enter the file corresponding to the source code repository, and the developer will submit the modified (added) document in the form of pr, After we receive the pr, we will conduct a document review, and once the review is passed, the document can be updated. What documents can I contribute? Documentation errors The documentation is not standardized and incomplete Go-zero application practice and experience Component Center How soon will the document be updated after the document pr is passed? After pr accepts, github action will automatically build gitbook and release, so you can view the updated document 1-2 minutes after github action is successful. Documentation contribution notes Error correction and improvement of the source document can directly write the original md file The newly added component documents need to be typeset and easy to read, and the component documents need to be placed in the Components subdirectory Go-zero application practice sharing can be directly placed in the Development Practice subdirectory Directory structure specification The directory structure should not be too deep, preferably no more than 3 levels The component document needs to be attributed to [Component Center] (component-center.md), such as Development Practice logx bloom executors Your document directory name Application practice needs to be attributed to Development Practice, such as Development Practice [How do I use go-zero to implement a middle-office system] (datacenter.md) Stream data processing tool [Summary of online communication issues on October 3](online-exchange.md Your document directory name Development Practice Document Template # Title > Author:The author name > > Original link: The original link some markdown content Guess you wants Join Us Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error.html":{"url":"error.html","title":"Error","keywords":"","body":"Error [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Error reporting on Windows A required privilege is not held by the client. ```text Solution: \"Run as administrator\" goctl will work. ## grpc error * Case 1 ```text protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. Solution: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go installation failed go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout Please make sure GOPROXY has been set, see Go Module Configuration for GOPROXY setting api service failed to start error: config file etc/user-api.yaml, error: type mismatch for field xx Please confirm whether the configuration items in the user-api.yaml configuration file have been configured. If there are values, check whether the yaml configuration file conforms to the yaml format. command not found: goctl command not found: goctl Please make sure that goctl has been installed or whether goctl has been added to the environment variable Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"source.html":{"url":"source.html","title":"Source Code","keywords":"","body":"Source Code [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR demo Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "}} \ No newline at end of file diff --git a/en/service-deployment.html b/en/service-deployment.html index 0fa67c1b4..b40a73830 100644 --- a/en/service-deployment.html +++ b/en/service-deployment.html @@ -1575,7 +1575,7 @@

    Guess you wants

  • etcd
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1620,7 +1620,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Service Deployment","level":"1.7.7","depth":2,"next":{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"previous":{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-deployment.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Service Deployment","level":"1.7.7","depth":2,"next":{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"previous":{"title":"CI/CD","level":"1.7.6","depth":2,"path":"ci-cd.md","ref":"ci-cd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-deployment.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/service-design.html b/en/service-design.html index 951265e36..488ccfd2c 100644 --- a/en/service-design.html +++ b/en/service-design.html @@ -1432,7 +1432,7 @@

    Guess you wants

  • API Directory Structure
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1477,7 +1477,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"next":{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"previous":{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-design.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Directory Structure","level":"1.7.5.1","depth":3,"next":{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},"previous":{"title":"Business Development","level":"1.7.5","depth":2,"path":"business-dev.md","ref":"business-dev.md","articles":[{"title":"Directory Structure","level":"1.7.5.1","depth":3,"path":"service-design.md","ref":"service-design.md","articles":[]},{"title":"Model Generation","level":"1.7.5.2","depth":3,"path":"model-gen.md","ref":"model-gen.md","articles":[]},{"title":"API Coding","level":"1.7.5.3","depth":3,"path":"api-coding.md","ref":"api-coding.md","articles":[]},{"title":"Business Coding","level":"1.7.5.4","depth":3,"path":"business-coding.md","ref":"business-coding.md","articles":[]},{"title":"JWT","level":"1.7.5.5","depth":3,"path":"jwt.md","ref":"jwt.md","articles":[]},{"title":"Middleware","level":"1.7.5.6","depth":3,"path":"middleware.md","ref":"middleware.md","articles":[]},{"title":"RPC Implement & Call","level":"1.7.5.7","depth":3,"path":"rpc-call.md","ref":"rpc-call.md","articles":[]},{"title":"Error Handling","level":"1.7.5.8","depth":3,"path":"error-handle.md","ref":"error-handle.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-design.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/service-monitor.html b/en/service-monitor.html index 8c711ec1f..afce9386e 100644 --- a/en/service-monitor.html +++ b/en/service-monitor.html @@ -1431,7 +1431,7 @@

    grafana dashboard

    Summary

    The above demonstrates the simple process of go-zero based on prometheus+grafana service indicator monitoring. In the production environment, different dimensions of monitoring and analysis can be done according to the actual scenario. Now go-zero's monitoring indicators are mainly for http and rpc, which is obviously insufficient for the overall monitoring of the service, such as the monitoring of container resources, the monitoring of dependent mysql, redis and other resources, and the monitoring of custom indicators, etc. Go-zero will continue to optimize in this regard. Hope this article can help you

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1476,7 +1476,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Monitor","level":"1.7.10","depth":2,"next":{"title":"Goctl","level":"1.8","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"previous":{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-monitor.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Monitor","level":"1.7.10","depth":2,"next":{"title":"Goctl","level":"1.8","depth":1,"path":"goctl.md","ref":"goctl.md","articles":[{"title":"Auto Completion","level":"1.8.1","depth":2,"path":"goctl-completion.md","ref":"goctl-completion.md","articles":[]},{"title":"Commands & Flags","level":"1.8.2","depth":2,"path":"goctl-commands.md","ref":"goctl-commands.md","articles":[]},{"title":"API Commands","level":"1.8.3","depth":2,"path":"goctl-api.md","ref":"goctl-api.md","articles":[]},{"title":"RPC Commands","level":"1.8.4","depth":2,"path":"goctl-rpc.md","ref":"goctl-rpc.md","articles":[]},{"title":"Model Commands","level":"1.8.5","depth":2,"path":"goctl-model.md","ref":"goctl-model.md","articles":[]},{"title":"Plugin Commands","level":"1.8.6","depth":2,"path":"goctl-plugin.md","ref":"goctl-plugin.md","articles":[]},{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]}]},"previous":{"title":"Trace","level":"1.7.9","depth":2,"path":"trace.md","ref":"trace.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"service-monitor.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/source.html b/en/source.html index 7fa9990a8..5cebfdf29 100644 --- a/en/source.html +++ b/en/source.html @@ -1353,7 +1353,7 @@

    Source Code

  • demo
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1394,7 +1394,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Source Code","level":"1.17","depth":1,"previous":{"title":"Error","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"source.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Source Code","level":"1.17","depth":1,"previous":{"title":"Error","level":"1.16","depth":1,"path":"error.md","ref":"error.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"source.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/stream.html b/en/stream.html index 54c257166..7b023fcae 100644 --- a/en/stream.html +++ b/en/stream.html @@ -1683,7 +1683,7 @@

    Concurrent processing

    Summary

    This article introduces the basic concepts of stream processing and the stream processing tool fx in go-zero. There are many stream processing scenarios in actual production. I hope this article can give you some inspiration and better response Stream processing scene at work.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1728,7 +1728,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Stream Handler","level":"1.13.5","depth":2,"next":{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"previous":{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"stream.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Stream Handler","level":"1.13.5","depth":2,"next":{"title":"Online Exchange","level":"1.13.6","depth":2,"path":"online-exchange.md","ref":"online-exchange.md","articles":[]},"previous":{"title":"Middle Ground System","level":"1.13.4","depth":2,"path":"datacenter.md","ref":"datacenter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"stream.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/template-cmd.html b/en/template-cmd.html index 367b8845f..6ee9ecafa 100644 --- a/en/template-cmd.html +++ b/en/template-cmd.html @@ -1443,7 +1443,7 @@

    Example

    Done
     
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1488,7 +1488,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Command","level":"1.9.1","depth":2,"next":{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"previous":{"title":"Template","level":"1.9","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-cmd.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Command","level":"1.9.1","depth":2,"next":{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]},"previous":{"title":"Template","level":"1.9","depth":1,"path":"template-manage.md","ref":"template-manage.md","articles":[{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},{"title":"Custom","level":"1.9.2","depth":2,"path":"template.md","ref":"template.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-cmd.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/template-manage.html b/en/template-manage.html index cc0e79caa..da2b8b0f6 100644 --- a/en/template-manage.html +++ b/en/template-manage.html @@ -1352,7 +1352,7 @@

    Template

  • Custom
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1397,7 +1397,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Template","level":"1.9","depth":1,"next":{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"previous":{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-manage.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Template","level":"1.9","depth":1,"next":{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"previous":{"title":"More Commands","level":"1.8.7","depth":2,"path":"goctl-other.md","ref":"goctl-other.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template-manage.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/template.html b/en/template.html index 823123c7f..ddaa21c38 100644 --- a/en/template.html +++ b/en/template.html @@ -1500,7 +1500,7 @@

    Summary

    ...
    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1545,7 +1545,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Custom","level":"1.9.2","depth":2,"next":{"title":"Extended","level":"1.10","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]}]},"previous":{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Custom","level":"1.9.2","depth":2,"next":{"title":"Extended","level":"1.10","depth":1,"path":"extended-reading.md","ref":"extended-reading.md","articles":[{"title":"logx","level":"1.10.1","depth":2,"path":"logx.md","ref":"logx.md","articles":[]},{"title":"bloom","level":"1.10.2","depth":2,"path":"bloom.md","ref":"bloom.md","articles":[]},{"title":"executors","level":"1.10.3","depth":2,"path":"executors.md","ref":"executors.md","articles":[]},{"title":"fx","level":"1.10.4","depth":2,"path":"fx.md","ref":"fx.md","articles":[]},{"title":"mysql","level":"1.10.5","depth":2,"path":"mysql.md","ref":"mysql.md","articles":[]},{"title":"redis-lock","level":"1.10.6","depth":2,"path":"redis-lock.md","ref":"redis-lock.md","articles":[]},{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]}]},"previous":{"title":"Command","level":"1.9.1","depth":2,"path":"template-cmd.md","ref":"template-cmd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"template.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/timing-wheel.html b/en/timing-wheel.html index 3f7003e7a..9facbd3d3 100644 --- a/en/timing-wheel.html +++ b/en/timing-wheel.html @@ -1584,7 +1584,7 @@

    Summary

    In terms of time separation, the time wheel has circle layers, so that the original numSlots can be reused continuously, because the timer is constantly loop, and execution can drop the upper layer slot to the lower layer. You can execute the task up to the upper level continuously in the loop.

    There are many useful component tools in go-zero. Good use of tools is of great help to improve service performance and development efficiency. I hope this article can bring you some gains.

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1629,7 +1629,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"TimingWheel","level":"1.10.9","depth":2,"next":{"title":"Around","level":"1.11","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"previous":{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"timing-wheel.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"TimingWheel","level":"1.10.9","depth":2,"next":{"title":"Around","level":"1.11","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"previous":{"title":"tokenlimit","level":"1.10.8","depth":2,"path":"tokenlimit.md","ref":"tokenlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"timing-wheel.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/tokenlimit.html b/en/tokenlimit.html index 5ecac8e51..d42e2e072 100644 --- a/en/tokenlimit.html +++ b/en/tokenlimit.html @@ -1497,7 +1497,7 @@

    Reference

  • Redis Rate
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1542,7 +1542,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"tokenlimit","level":"1.10.8","depth":2,"next":{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"previous":{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tokenlimit.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"tokenlimit","level":"1.10.8","depth":2,"next":{"title":"TimingWheel","level":"1.10.9","depth":2,"path":"timing-wheel.md","ref":"timing-wheel.md","articles":[]},"previous":{"title":"periodlimit","level":"1.10.7","depth":2,"path":"periodlimit.md","ref":"periodlimit.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tokenlimit.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/tool-center.html b/en/tool-center.html index 17b2e8f22..44cdd9630 100644 --- a/en/tool-center.html +++ b/en/tool-center.html @@ -1358,7 +1358,7 @@

    Tools

  • VSCode plugin
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1403,7 +1403,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Tools","level":"1.11.1","depth":2,"next":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"previous":{"title":"Around","level":"1.11","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tool-center.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Tools","level":"1.11.1","depth":2,"next":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"previous":{"title":"Around","level":"1.11","depth":1,"path":"eco.md","ref":"eco.md","articles":[{"title":"Tools","level":"1.11.1","depth":2,"path":"tool-center.md","ref":"tool-center.md","articles":[{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"path":"vscode.md","ref":"vscode.md","articles":[]}]},{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},{"title":"Plugins","level":"1.11.3","depth":2,"path":"plugin-center.md","ref":"plugin-center.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"tool-center.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/trace.html b/en/trace.html index 73717725d..5f5eff286 100644 --- a/en/trace.html +++ b/en/trace.html @@ -1505,7 +1505,7 @@

    Reference

  • go-zero trace
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1550,7 +1550,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Trace","level":"1.7.9","depth":2,"next":{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"previous":{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"trace.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Trace","level":"1.7.9","depth":2,"next":{"title":"Monitor","level":"1.7.10","depth":2,"path":"service-monitor.md","ref":"service-monitor.md","articles":[]},"previous":{"title":"Log Collection","level":"1.7.8","depth":2,"path":"log-collection.md","ref":"log-collection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"trace.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/vscode.html b/en/vscode.html index 82e9b7d74..48e491308 100644 --- a/en/vscode.html +++ b/en/vscode.html @@ -1375,7 +1375,7 @@

    service block

    handler block

    type

    Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1420,7 +1420,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"next":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"previous":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"vscode.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"VSCode Plugin","level":"1.11.1.2","depth":3,"next":{"title":"Distributed Transaction","level":"1.11.2","depth":2,"path":"distributed-transaction.md","ref":"distributed-transaction.md","articles":[]},"previous":{"title":"Intellij Plugin","level":"1.11.1.1","depth":3,"path":"intellij.md","ref":"intellij.md","articles":[]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"vscode.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/en/wechat.html b/en/wechat.html index 7c8718441..2f508a794 100644 --- a/en/wechat.html +++ b/en/wechat.html @@ -1379,7 +1379,7 @@

    Articles(Simplified Chinese)

  • 《服务自适应降载保护设计》
  • Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: -2024-03-01 16:09:17 +2024-03-02 02:15:50
    @@ -1424,7 +1424,7 @@

    No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"Wechat","level":"1.12.1","depth":2,"next":{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"previous":{"title":"Learning Resources","level":"1.12","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"wechat.md","mtime":"2024-03-01T16:09:17.481Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-01T16:10:06.666Z"},"basePath":".","book":{"language":"en"}}); + gitbook.page.hasChanged({"page":{"title":"Wechat","level":"1.12.1","depth":2,"next":{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},"previous":{"title":"Learning Resources","level":"1.12","depth":1,"path":"learning-resource.md","ref":"learning-resource.md","articles":[{"title":"Wechat","level":"1.12.1","depth":2,"path":"wechat.md","ref":"wechat.md","articles":[]},{"title":"Night","level":"1.12.2","depth":2,"path":"goreading.md","ref":"goreading.md","articles":[]},{"title":"OpenTalk","level":"1.12.3","depth":2,"path":"gotalk.md","ref":"gotalk.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["back-to-top-button","chapter-fold","code","-lunr","-search","search-pro","github","splitter","-sharing","sharing-plus","tbfed-pagefooter","flexible-alerts","page-toc-button","pageview-count","popup","hide-element","edit-link","-highlight","prism","theme-comscore"],"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © 2019-2021 go-zero","modify_label":"Last UpdateTime:","modify_format":"YYYY-MM-DD HH:mm:ss"},"chapter-fold":{},"prism":{"lang":{"shell":"bash"},"css":["prismjs/themes/prism-tomorrow.css"]},"github":{"url":"https://github.com/zeromicro/go-zero"},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"code":{"copyButtons":true},"hide-element":{"elements":[".gitbook-link"]},"fontsettings":{"theme":"white","family":"sans","size":2},"theme-comscore":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"flexible-alerts":{"danger":{"className":"danger","icon":"fa fa-ban","label":"Attention"},"note":{"className":"info","icon":"fa fa-info-circle","label":"Note"},"style":"callout","tip":{"className":"tip","icon":"fa fa-lightbulb-o","label":"Tip"},"warning":{"className":"warning","icon":"fa fa-exclamation-triangle","label":"Warning"}},"sharing":{"qq":false,"all":["douban","facebook","google","linkedin","twitter","weibo","qq","qzone","weibo"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":false,"messenger":false,"line":true,"vk":false,"pocket":false,"google":true,"viber":false,"stumbleupon":false,"qzone":true,"linkedin":true},"edit-link":{"label":"EDIT THIS PAGE","base":"https://github.com/zeromicro/zero-doc/tree/main/go-zero.dev"},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"anqiansong","pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"go-zero document","language":"en","gitbook":"*","description":"Golang 微服务框架 | 集成各种工程实践的 WEB 和 RPC 框架 | 一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码"},"file":{"path":"wechat.md","mtime":"2024-03-02T02:15:50.662Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-03-02T02:16:38.885Z"},"basePath":".","book":{"language":"en"}}); }); diff --git a/search_plus_index.json b/search_plus_index.json index b5b062748..7230ce682 100644 --- a/search_plus_index.json +++ b/search_plus_index.json @@ -1 +1 @@ -{"./":{"url":"./","title":"简介","keywords":"","body":" go-zero 缩短从需求到上线的距离 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 使用 go-zero 的好处: 轻松获得支撑千万日活服务的稳定性 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码 微服务治理中间件可无缝集成到其它现有框架使用 极简的 API 描述,一键生成各端代码 自动校验客户端请求参数合法性 大量微服务治理和并发工具包 1. go-zero 框架背景 18 年初,我们决定从 Java+MongoDB 的单体架构迁移到微服务架构,经过仔细思考和对比,我们决定: 基于 Go 语言 高效的性能 简洁的语法 广泛验证的工程效率 极致的部署体验 极低的服务端资源成本 自研微服务框架 有过很多微服务框架自研经验 需要有更快速的问题定位能力 更便捷的增加新特性 2. go-zero 框架设计思考 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 我们经历不到半年时间,彻底完成了从 Java+MongoDB 到 Golang+MySQL 为主的微服务体系迁移,并于 18 年 8 月底完全上线,稳定保障了业务后续迅速增长,确保了整个服务的高可用。 3. go-zero 项目实现和特点 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: 觉得不错的话,别忘 star 👏 4. Installation 在项目目录下通过如下命令安装: GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero 5. Quick Start 完整示例请查看 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 安装 goctl 工具 goctl 读作 go control,不要读成 go C-T-L。goctl 的意思是不要被代码控制,而是要去控制它。其中的 go 不是指 golang。在设计 goctl 之初,我就希望通过 她 来解放我们的双手👈 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 如果使用 go1.16 版本, 可以使用 go install 命令安装 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 确保 goctl 可执行 快速生成 api 服务 goctl api new greet cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: curl -i http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 22 Oct 2020 14:03:18 GMT Content-Length: 14 {\"message\":\"\"} 编写业务代码: api 文件定义了服务对外暴露的路由,可参考 api 规范 可以在 servicecontext.go 里面传递依赖给 logic,比如 mysql, redis 等 在 api 定义的 get/post/put/delete 等请求对应的 logic 里增加业务处理逻辑 可以根据 api 文件生成前端需要的 Java, TypeScript, Dart, JavaScript 代码 goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 6. Benchmark 测试代码见这里 7. 文档 API 文档 goctl 使用帮助 awesome 系列(更多文章见『微服务实践』公众号) 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 精选 goctl 插件 插件 用途 goctl-swagger 一键生成 api 的 swagger 文档 goctl-android 生成 java (android) 端 http client 请求代码 goctl-go-compact 合并 api 里同一个 group 里的 handler 到一个 go 文件 8. 微信公众号 go-zero 相关文章都会在 微服务实践 公众号整理呈现,欢迎扫码关注,也可以通过公众号私信我 👏 9. 微信交流群 如果文档中未能覆盖的任何疑问,欢迎您在群里提出,我们会尽快答复。 您可以在群内提出使用中需要改进的地方,我们会考虑合理性并尽快修改。 如果您发现 bug 请及时提 issue,我们会尽快确认并修改。 为了防止广告用户、识别技术同行,请 star 后加我时注明 github 当前 star 数,我再拉进 go-zero 群,感谢! 加我之前有劳点一下 star,一个小小的 star 是作者们回答海量问题的动力🤝 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"about-us.html":{"url":"about-us.html","title":"关于我们","keywords":"","body":"关于我们 go-zero go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 go-zero作者 万俊峰,七牛云技术副总裁,拥有14年研发团队管理经验,16年架构设计经验,20年工程实战经验,负责过多个大型项目的架构设计,曾多次合伙创业(被收购),阿里云MVP,ArchSummit全球架构师峰会明星讲师,GopherChina大会主持人 & 金牌讲师,QCon+ Go语言出品人兼讲师,腾讯云开发者大会讲师。 go-zero社区 我们目前拥有7000多人的社区成员,在这里,你可以和大家讨论任何关于go-zero的技术,问题反馈,获取最新的go-zero信息,以及各位大佬每天分享的技术心得。 go-zero社区群 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"join-us.html":{"url":"join-us.html","title":"加入我们","keywords":"","body":"加入我们 概要 go-zero 是一个基于MIT License 的开源项目,大家在使用中发现bug,有新的特性等,均可以参与到go-zero的贡献中来,我们非常欢迎大家的积极参与,也会最快响应大家提出的各种问题,pr等。 贡献形式 Pull Request Issue 贡献须知 go-zero 的Pull request中的代码需要满足一定规范 命名规范,请阅读命名规范 以英文注释为主 pr时备注好功能特性,描述需要清晰,简洁 增加单元测试覆盖率达80%+ 贡献代码(pr) 进入go-zero 项目,fork一份go-zero 项目到自己的github仓库中。 回到自己的github主页,找到xx/go-zero项目,其中xx为你的用户名,如anqiansong/go-zero 克隆代码到本地 开发代码,push到自己的github仓库 进入自己的github中go-zero项目,点击浮层上的的【Pull requests】进入Compare页面。 base repository选择zeromicro/go-zero base:master,head repository选择xx/go-zero compare:$branch ,$branch为你开发的分支,如图: 点击【Create pull request】即可实现pr申请 确认pr是否提交成功,进入go-zero 的Pull requests 查看,应该有自己提交的记录,名称为你的开发时的分支名称 Issue 在我们的社区中,有很多伙伴会积极的反馈一些go-zero使用过程中遇到的问题,由于社区人数较多,我们虽然会实时的关注社区动态,但大家问题反馈过来都是随机的,当我们团队还在解决某一个伙伴提出的问题时,另外的问题也反馈上来,可能会导致团队会很容易忽略掉,为了能够一一的解决大家的问题,我们强烈建议大家通过issue的方式来反馈问题,包括但不限于bug,期望的新功能特性等,我们在实现某一个新特性时也会在issue中体现,大家在这里也能够在这里获取到go-zero的最新动向,也欢迎大家来积极的参与讨论。 怎么提Issue 点击这里 进入go-zero的Issue页面或者直接访问https://github.com/zeromicro/go-zero/issues 地址 点击右上角的【New issue】新建issue 填写issue标题和内容 点击【Submit new issue】提交issue 参考文档 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"概念介绍","keywords":"","body":"概念介绍 go-zero 晓黑板golang开源项目,集各种工程实践于一身的web和rpc框架。 goctl 一个旨在为开发人员提高工程效率、降低出错率的辅助工具。 goctl插件 指以goctl为中心的周边二进制资源,能够满足一些个性化的代码生成需求,如路由合并插件goctl-go-compact插件, 生成swagger文档的goctl-swagger插件,生成php调用端的goctl-php插件等。 intellij/vscode插件 在intellij系列产品上配合goctl开发的插件,其将goctl命令行操作使用UI进行替代。 api文件 api文件是指用于定义和描述api服务的文本文件,其以.api后缀结尾,包含api语法描述内容。 goctl环境 goctl环境是使用goctl前的准备环境,包含 golang环境 protoc protoc-gen-go插件 go module | gopath go-zero-demo go-zero-demo里面包含了文档中所有源码的一个大仓库,后续我们在编写演示demo时,我们均在此项目下创建子项目, 因此我们需要提前创建一个大仓库go-zero-demo,我这里把这个仓库放在home目录下。 $ cd ~ $ mkdir go-zero-demo&&cd go-zero-demo $ go mod init go-zero-demo 参考文档 go-zero Goctl 插件中心 工具中心 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"quick-start.html":{"url":"quick-start.html","title":"快速开发","keywords":"","body":"快速开发 本节主要通过对 api/rpc 等服务快速开始来让大家对使用 go-zero 开发的工程有一个宏观概念,更加详细的介绍我们将在后续一一展开。如果您已经参考 准备工作 做好环境及工具的准备,请跟随以下小节开始体验: 单体服务 微服务 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"单体服务","keywords":"","body":"单体服务 前言 由于go-zero集成了web/rpc于一体,社区有部分小伙伴会问我,go-zero的定位是否是一款微服务框架,答案是不止于此, go-zero虽然集众多功能于一身,但你可以将其中任何一个功能独立出来去单独使用,也可以开发单体服务, 不是说每个服务上来就一定要采用微服务的架构的设计,这点大家可以看看作者(kevin)的第四期开源说 ,其中对此有详细的讲解。 创建greet服务 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 查看一下greet服务的目录结构 $ tree greet greet ├── etc │ └── greet-api.yaml ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go 由以上目录结构可以观察到,greet服务虽小,但\"五脏俱全\"。接下来我们就可以在greetlogic.go中编写业务代码了。 编写逻辑 $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req *types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } 启动并访问服务 启动服务 $ cd greet $ go run greet.go -f etc/greet-api.yaml 输出如下,服务启动并侦听在8888端口: Starting server at 0.0.0.0:8888... 访问服务 $ curl -i -X GET http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} 源码 greet源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"micro-service.html":{"url":"micro-service.html","title":"微服务","keywords":"","body":"微服务 在上一篇我们已经演示了怎样快速创建一个单体服务,接下来我们来演示一下如何快速创建微服务, 在本小节中,api部分其实和单体服务的创建逻辑是一样的,只是在单体服务中没有服务间的通讯而已, 且微服务中api服务会多一些rpc调用的配置。 前言 本小节将以一个订单服务调用用户服务来简单演示一下,演示代码仅传递思路,其中有些环节不会一一列举。 情景提要 假设我们在开发一个商城项目,而开发者小明负责用户模块(user)和订单模块(order)的开发,我们姑且将这两个模块拆分成两个微服务① [注意] ①:微服务的拆分也是一门学问,这里我们就不讨论怎么去拆分微服务的细节了。 演示功能目标 订单服务(order)提供一个查询接口 用户服务(user)提供一个方法供订单服务获取用户信息 服务设计分析 根据情景提要我们可以得知,订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯,相信这里我已经对\"rpc服务存在有什么作用?\"已经作了一个比较好的场景描述。 当然,一个服务开发前远不止这点设计分析,我们这里就不详细描述了。从上文得知,我们需要一个 user rpc order api 两个服务来初步实现这个小demo。 创建mall工程 如果你跑了单体的示例,里面也叫 go-zero-demo,你可能需要换一个父目录。 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 创建user rpc服务 创建user rpc服务 $ mkdir -p mall/user/rpc 添加user.proto文件,增加getUser方法 $ vim mall/user/rpc/user.proto 增加如下代码: syntax = \"proto3\"; package user; // protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成 option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // 用户id string id = 1; // 用户名称 string name = 2; // 用户性别 string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } 生成代码 如未安装 protoc,protoc-gen-go,protoc-gen-grpc-go 你可以通过如下指令一键安装: $ goctl env check -i -f 注意: 1、每一个 *.proto文件只允许有一个service error: only one service expected $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. 填充业务逻辑 $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } 创建order api服务 创建 order api服务 # 回到 go-zero-demo/mall 目录 $ mkdir -p order/api && cd order/api 添加api文件 $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } 生成order服务 $ goctl api go -api order.api -dir . Done. 添加user rpc配置 $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } 添加yaml配置 $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc 完善服务依赖 $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 添加order演示逻辑 给 getorderlogic 添加业务逻辑 $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } 启动服务并验证 启动etcd$ etcd 下载依赖# 在 go-zero-demo 目录下 $ go mod tidy 启动user rpc # 在 mall/user/rpc 目录 $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动order api # 在 mall/order/api 目录 $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... 访问order api $ curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\":\"1\",\"name\":\"test order\"} 注意:在演示中的提及的api语法,rpc生成,goctl,goctl环境等怎么使用和安装,快速入门中不作详细概述,我们后续都会有详细的文档进行描述,你也可以点击下文的【猜你想看】快速跳转的对应文档查看。 源码 mall源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 rpc目录 rpc配置 rpc调用方说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"framework-design.html":{"url":"framework-design.html","title":"框架设计","keywords":"","body":"框架设计 本节将从 go-zero 的设计理念,go-zero 服务的最佳实践目录来说明 go-zero 框架的设计,本节将包含以下小节: go-zero设计理念 go-zero特点 api语法介绍 api目录结构 rpc目录结构 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"go-zero设计理念","keywords":"","body":"go-zero设计理念 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"go-zero特点","keywords":"","body":"go-zero特性 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-grammar.html":{"url":"api-grammar.html","title":"api语法介绍","keywords":"","body":"api语法介绍 api示例 /** * api语法示例及语法说明 */ // api语法版本 syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"songmeizi\" date: \"2020-01-08\" desc: \"api语法示例及语法说明\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } api语法结构 syntax语法声明 import语法块 info语法块 type语法块 service语法块 隐藏通道 [!TIP] 在以上语法结构中,各个语法块从语法上来说,按照语法块为单位,可以在.api文件中任意位置声明, 但是为了提高阅读效率,我们建议按照以上顺序进行声明,因为在将来可能会通过严格模式来控制语法块的顺序。 syntax语法声明 syntax是新加入的语法结构,该语法的引入可以解决: 快速针对api版本定位存在问题的语法结构 针对版本做语法解析 防止api语法大版本升级导致前后不能向前兼容 **[!WARNING] 被import的api必须要和main api的syntax版本一致。 语法定义 'syntax'={checkVersion(p)}STRING 语法说明 syntax:固定token,标志一个syntax语法结构的开始 checkVersion:自定义go方法,检测STRING是否为一个合法的版本号,目前检测逻辑为,STRING必须是满足(?m)\"v[1-9][0-9]*\"正则。 STRING:一串英文双引号包裹的字符串,如\"v1\" 一个api语法文件只能有0或者1个syntax语法声明,如果没有syntax,则默认为v1版本 正确语法示例 ✅ eg1:不规范写法 syntax=\"v1\" eg2:规范写法(推荐) syntax = \"v2\" 错误语法示例 ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" import语法块 随着业务规模增大,api中定义的结构体和服务越来越多,所有的语法描述均为一个api文件,这是多么糟糕的一个问题, 其会大大增加了阅读难度和维护难度,import语法块可以帮助我们解决这个问题,通过拆分api文件, 不同的api文件按照一定规则声明,可以降低阅读难度和维护难度。 **[!WARNING] 这里import不像golang那样包含package声明,仅仅是一个文件路径的引入,最终解析后会把所有的声明都汇聚到一个spec.Spec中。 不能import多个相同路径,否则会解析错误。 语法定义 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' 语法说明 import:固定token,标志一个import语法的开始 checkImportValue:自定义go方法,检测STRING是否为一个合法的文件路径,目前检测逻辑为,STRING必须是满足(?m)\"(/?[a-zA-Z0-9_#-])+\\.api\"正则。 STRING:一串英文双引号包裹的字符串,如\"foo.api\" 正确语法示例 ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) 错误语法示例 ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info语法块 info语法块是一个包含了多个键值对的语法体,其作用相当于一个api服务的描述,解析器会将其映射到spec.Spec中, 以备用于翻译成其他语言(golang、java等) 时需要携带的meta元素。如果仅仅是对当前api的一个说明,而不考虑其翻译 时传递到其他语言,则使用简单的多行注释或者java风格的文档注释即可,关于注释说明请参考下文的 隐藏通道。 **[!WARNING] 不能使用重复的key,每个api文件只能有0或者1个info语法块 语法定义 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' 语法说明 info:固定token,标志一个info语法块的开始 checkKeyValue:自定义go方法,检测VALUE是否为一个合法值。 VALUE:key对应的值,可以为单行的除'\\r','\\n','/'后的任意字符,多行请以\"\"包裹,不过强烈建议所有都以\"\"包裹 正确语法示例 ✅ eg1:不规范写法 info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:规范写法(推荐) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) 错误语法示例 ❌ eg1:没有key-value内容 info() eg2:不包含冒号 info( foo value ) eg3:key-value没有换行 info(foo:\"value\") eg4:没有key info( : \"value\" ) eg5:非法的key info( 12: \"value\" ) eg6:移除旧版本多行语法 info( foo: > some text type语法块 在api服务中,我们需要用到一个结构体(类)来作为请求体,响应体的载体,因此我们需要声明一些结构体来完成这件事情, type语法块由golang的type演变而来,当然也保留着一些golang type的特性,沿用golang特性有: 保留了golang内置数据类型bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, 兼容golang struct风格声明 保留golang关键字 **[!WARNING]️ 不支持alias 不支持time.Time数据类型,用int64表示,因为api支持客户端代码生成,并非所有客户端语言都有time.Time对应的类型 结构体名称、字段名称、不能为golang关键字 语法定义 由于其和golang相似,因此不做详细说明,具体语法定义请在 ApiParser.g4 中查看typeSpec定义。 语法说明 参考golang写法 正确语法示例 ✅ eg1:不规范写法 type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // 非导出型字段 bar int `form:\"bar\"` } type( // 非导出型结构体 fooBar struct{ FooBar int `json:\"fooBar\"` } ) eg2:规范写法(推荐) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int `json:\"fooBar\"` } ) 错误语法示例 ❌ eg type Gender int // 不支持 // 非struct token type Foo structure{ CreateTime time.Time // 不支持time.Time,且没有声明 tag } // golang关键字 var type var{} type Foo{ // golang关键字 interface Foo interface // 没有声明 tag } type Foo{ foo int // map key必须要golang内置数据类型,且没有声明 tag m map[Bar]string } [!NOTE] ① tag定义和golang中json tag语法一样,除了json tag外,go-zero还提供了另外一些tag来实现对字段的描述, 详情见下表。 tag表 绑定参数时,以下四个tag只能选择其中一个 tag key 描述 提供方有效范围 示例 json json序列化tag golang request、response json:\"fooo\" path 路由path,如/foo/:id go-zero request path:\"id\" form 标志请求体是一个form(POST方法时)或者一个query(GET方法时/search?name=keyword) go-zero request form:\"name\" header HTTP header,如 Name: value go-zero request header:\"name\" tag修饰符 常见参数校验描述 tag key 描述 提供方 有效范围 示例 optional 定义当前字段为可选参数 go-zero request json:\"name,optional\" options 定义当前字段的枚举值,多个以竖线|隔开 go-zero request json:\"gender,options=male\" default 定义当前字段默认值 go-zero request json:\"gender,default=male\" range 定义当前字段数值范围 go-zero request json:\"age,range=[0:120]\" [!TIP] tag修饰符需要在tag value后以英文逗号,隔开 service语法块 service语法块用于定义api服务,包含服务名称,服务metadata,中间件声明,路由,handler等。 **[!WARNING]️ main api和被import的api服务名称必须一致,不能出现服务名称歧义。 handler名称不能重复 路由(请求方法+请求path)名称不能重复 请求体必须声明为普通(非指针)struct,响应体做了一些向前兼容处理,详请见下文说明 语法定义 serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; 语法说明 serviceSpec:包含了一个可选语法块atServer和serviceApi语法块,其遵循序列模式(编写service必须要按照顺序,否则会解析出错) atServer: 可选语法块,定义key-value结构的server metadata,'@server' 表示这一个server语法块的开始,其可以用于描述serviceApi或者route语法块,其用于描述不同语法块时有一些特殊关键key 需要值得注意,见 atServer关键key描述说明。 serviceApi:包含了1到多个serviceRoute语法块 serviceRoute:按照序列模式包含了atDoc,handler和route atDoc:可选语法块,一个路由的key-value描述,其在解析后会传递到spec.Spec结构体,如果不关心传递到spec.Spec, 推荐用单行注释替代。 handler:是对路由的handler层描述,可以通过atServer指定handler key来指定handler名称, 也可以直接用atHandler语法块来定义handler名称 atHandler:'@handler' 固定token,后接一个遵循正则[_a-zA-Z][a-zA-Z_-]*)的值,用于声明一个handler名称 route:路由,有httpMethod、path、可选request、可选response组成,httpMethod是必须是小写。 body:api请求体语法定义,必须要由()包裹的可选的ID值 replyBody:api响应体语法定义,必须由()包裹的struct、array(向前兼容处理,后续可能会废弃,强烈推荐以struct包裹,不要直接用array作为响应体) kvLit: 同info key-value serviceName: 可以有多个'-'join的ID值 path:api请求路径,必须以'/'或者'/:'开头,切不能以'/'结尾,中间可包含ID或者多个以'-'join的ID字符串 atServer关键key描述说明 修饰service时 key描述示例 jwt声明当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码jwt: Auth group声明当前service或者路由文件分组group: login middleware声明当前service需要开启中间件middleware: AuthMiddleware prefix添加路由分组prefix: api 修饰route时 key描述示例 handler声明一个handler- 正确语法示例 ✅ eg1:不规范写法 @server( jwt: Auth group: foo middleware: AuthMiddleware prefix api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // 非导出型body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// 不推荐数组作为响应体 @handler fooBar post /foo/bar (Foo) returns // 可以省略'returns' } eg2:规范写法(推荐) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } 错误语法示例 ❌ // 不支持空的server语法块 @server( ) // 不支持空的service语法块 service foo-api{ } service foo-api{ @doc kkkk // 简版doc必须用英文双引号引起来 @handler foo post /foo @handler foo // 重复的handler post /bar @handler fooBar post /bar // 重复的路由 // @handler和@doc顺序错误 @handler someHandler @doc \"some doc\" post /some/path // handler缺失 post /some/path/:id @handler reqTest post /foo/req (*Foo) // 不支持除普通结构体外的其他数据类型作为请求体 @handler replyTest post /foo/reply returns (*Foo) // 不支持除普通结构体、数组(向前兼容,后续考虑废弃)外的其他数据类型作为响应体 } 隐藏通道 隐藏通道目前主要为空白符号、换行符号以及注释,这里我们只说注释,因为空白符号和换行符号我们目前拿来也无用。 单行注释 语法定义 '//' ~[\\r\\n]* 语法说明 由语法定义可知道,单行注释必须要以//开头,内容为不能包含换行符 正确语法示例 ✅ // doc // comment 错误语法示例 ❌ // break line comments java风格文档注释 语法定义 '/*' .*? '*/' 语法说明 由语法定义可知道,单行注释必须要以/*开头,*/结尾的任意字符。 正确语法示例 ✅ /** * java-style doc */ 错误语法示例 ❌ /* * java-style doc */ */ Doc&Comment 如果想获取某一个元素的doc或者comment开发人员需要怎么定义? Doc 我们规定上一个语法块(非隐藏通道内容)的行数line+1到当前语法块第一个元素前的所有注释(单行,或者多行)均为doc, 且保留了//、/*、*/原始标记。 Comment 我们规定当前语法块最后一个元素所在行开始的一个注释块(当行,或者多行)为comment 且保留了//、/*、*/原始标记。 语法块Doc和Comment的支持情况 语法块parent语法块DocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ 以下为对应语法块解析后细带doc和comment的写法 // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * 开启jwt鉴权 */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * route doc * post请求 * path为 /foo * 请求体:Foo * 响应体:Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-dir.html":{"url":"api-dir.html","title":"api目录结构","keywords":"","body":"api目录介绍 . ├── etc │ └── greet-api.yaml // 配置文件 ├── go.mod // mod文件 ├── greet.api // api描述文件 ├── greet.go // main函数入口 └── internal ├── config │ └── config.go // 配置声明type ├── handler // 路由及handler转发 │ ├── greethandler.go │ └── routes.go ├── logic // 业务逻辑 │ └── greetlogic.go ├── middleware // 中间件文件 │ └── greetmiddleware.go ├── svc // logic所依赖的资源池 │ └── servicecontext.go └── types // request、response的struct,根据api自动生成,不建议编辑 └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"rpc目录结构","keywords":"","body":"rpc服务目录 proto 文件 greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations! your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. 生成的目录结构 . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] pb.go & _grpc.pb.go 文件所在目录并非固定,该目录有 go_opt & go-grpc_opt 与 proto文件中的 go_package 值共同决定,想要了解grpc代码生成目录逻辑请阅读 Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"project-dev.html":{"url":"project-dev.html","title":"项目开发","keywords":"","body":"项目开发 在前面的章节我们已经从一些概念、背景、快速入门等维度介绍了一下go-zero,看到这里,相信你对go-zero已经有了一些了解, 从这里开始,我们将会从环境准备到服务部署整个流程开始进行讲解,为了保证大家能够彻底弄懂go-zero的开发流程,那就准备你的耐心来接着往下走吧。 在章节中,将包含以下小节: 准备工作 golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 开发规范 命名规范 路由规范 编码规范 开发流程 配置介绍 api配置 rpc配置 业务开发 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 CI/CD 服务部署 日志收集 链路追踪 服务监控 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare.html":{"url":"prepare.html","title":"准备工作","keywords":"","body":"准备工作 在正式进入实际开发之前,我们需要做一些准备工作,比如:Go环境的安装,grpc代码生成使用的工具安装, 必备工具Goctl的安装,Golang环境配置等,本节将包含以下小节: golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"golang-install.html":{"url":"golang-install.html","title":"golang安装","keywords":"","body":"Golang环境安装 前言 开发golang程序,必然少不了对其环境的安装,我们这里选择以1.15.1为例。 官方文档 https://golang.google.cn/doc/install mac OS安装Go 下载并安装Go for Mac 验证安装结果 $ go version go version go1.15.1 darwin/amd64 linux 安装Go 下载Go for Linux 解压压缩包至/usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz 添加/usr/local/go/bin到环境变量 $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile 验证安装结果 $ go version go version go1.15.1 linux/amd64 Windows安装Go 下载并安装Go for Windows 验证安装结果 $ go version go version go1.15.1 windows/amd64 其他 更多操作系统安装见https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gomod-config.html":{"url":"gomod-config.html","title":"go module配置","keywords":"","body":"Go Module设置 Go Module介绍 Modules are how Go manages dependencies.[1] 即Go Module是Golang管理依赖性的方式,像Java中的Maven,Android中的Gradle类似。 MODULE配置 查看GO111MODULE开启情况 $ go env GO111MODULE on 开启GO111MODULE,如果已开启(即执行go env GO111MODULE结果为on)请跳过。 $ go env -w GO111MODULE=\"on\" 设置GOPROXY $ go env -w GOPROXY=https://goproxy.cn 设置GOMODCACHE 查看GOMODCACHE $ go env GOMODCACHE 如果目录不为空或者/dev/null,请跳过。 go env -w GOMODCACHE=$GOPATH/pkg/mod 参考文档 [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-install.html":{"url":"goctl-install.html","title":"goctl安装","keywords":"","body":"Goctl安装 前言 Goctl在go-zero项目开发着有着很大的作用,其可以有效的帮助开发者大大提高开发效率,减少代码的出错率,缩短业务开发的工作量,更多的Goctl的介绍请阅读Goctl介绍, 在这里我们强烈推荐大家安装,因为后续演示例子中我们大部分都会以goctl进行演示。 安装(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 环境变量检测 go get下载编译后的二进制文件位于$GOPATH/bin目录下,要确保$GOPATH/bin已经添加到环境变量。 $ sudo vim /etc/paths 在最后一行添加如下内容 $GOPATH/bin [!TIP] $GOPATH为你本机上的文件地址 安装结果验证 $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] windows用户添加环境变量请自行google Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go安装","keywords":"","body":"protoc & protoc-gen-go安装 前言 protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。 mac OS方式一:goctl一键安装 $ goctl env check -i -f --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! 方式二: 源文件安装 protoc安装 进入protobuf release 页面,选择适合自己操作系统的压缩包文件 解压protoc-x.x.x-osx-x86_64.zip并进入protoc-x.x.x-osx-x86_64 $ cd protoc-x.x.x-osx-x86_64/bin 将启动的protoc二进制文件移动到被添加到环境变量的任意path下,如$GOPATH/bin,这里不建议直接将其和系统的以下path放在一起。 $ mv protoc $GOPATH/bin [!TIP] $GOPATH为你本机的实际文件夹地址 验证安装结果 $ protoc --version libprotoc x.x.x protoc-gen-go/protoc-gen-go-grpc 安装 下载安装protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest [!WARNING] protoc-gen-go安装失败请阅读常见错误处理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"prepare-other.html":{"url":"prepare-other.html","title":"其他","keywords":"","body":"其他 在之前我们已经对Go环境、Go Module配置、Goctl、protoc & protoc-gen-go安装准备就绪,这些是开发人员在开发阶段必须要准备的环境,而接下来的环境你可以选择性的安装, 因为这些环境一般存在于服务器(安装工作运维会替你完成),但是为了后续演示流程能够完整走下去,我建议大家在本地也安装一下,因为我们的演示环境大部分会以本地为主。 以下仅给出了需要的准备工作,不以文档篇幅作详细介绍了。 其他环境 etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-specification.html":{"url":"dev-specification.html","title":"开发规范","keywords":"","body":"开发规范 在实际业务开发中,除了要提高业务开发效率,缩短业务开发周期,保证线上业务高性能,高可用的指标外,好的编程习惯也是一个开发人员基本素养之一,在本章节, 我们将介绍一下go-zero中的编码规范,本章节为可选章节,内容仅供交流与参考,本章节将从以下小节进行说明: 命名规范 路由规范 编码规范 开发三原则 Clarity(清晰) 作者引用了Hal Abelson and Gerald Sussman的一句话: Programs must be written for people to read, and only incidentally for machines to execute 程序是什么,程序必须是为了开发人员阅读而编写的,只是偶尔给机器去执行,99%的时间程序代码面向的是开发人员,而只有1%的时间可能是机器在执行,这里比例不是重点,从中我们可以看出,清晰的代码是多么的重要,因为所有程序,不仅是Go语言,都是由开发人员编写,供其他人阅读和维护。 Simplicity(简单) Simplicity is prerequisite for reliability Edsger W. Dijkstra认为:可靠的前提条件就是简单,我们在实际开发中都遇到过,这段代码在写什么,想要完成什么事情,开发人员不理解这段代码,因此也不知道如何去维护,这就带来了复杂性,程序越是复杂就越难维护,越难维护就会是程序变得越来越复杂,因此,遇到程序变复杂时首先应该想到的是——重构,重构会重新设计程序,让程序变得简单。 Productivity(生产力) 在go-zero团队中,一直在强调这个话题,开发人员成产力的多少,并不是你写了多少行代码,完成了多少个模块开发,而是我们需要利用各种有效的途径来利用有限的时间完成开发效率最大化,而Goctl的诞生正是为了提高生产力, 因此这个开发原则我是非常认同的。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"naming-spec.html":{"url":"naming-spec.html","title":"命名规范","keywords":"","body":"命名规范 在任何语言开发中,都有其语言领域的一些命名规范,好的命名可以: 降低代码阅读成本 降低维护难度 降低代码复杂度 规范建议 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。 命名准则 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好。 变量命名应尽量描述其内容,而不是类型 常量命名应尽量描述其值,而不是如何使用这个值 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值 method、interface、type、package推荐使用单词命名 package名称也是命名的一部分,请尽量将其利用起来 使用一致的命名风格 文件命名规范 全部小写 除unit test外避免下划线(_) 文件名称不宜过长 变量命名规范参考 首字母小写 驼峰命名 见名知义,避免拼音替代英文 不建议包含下划线(_) 不建议包含数字 适用范围 局部变量 函数出参、入参 函数、常量命名规范 驼峰式命名 可exported的必须首字母大写 不可exported的必须首字母小写 避免全部大写与下划线(_)组合 [!TIP] 如果是go-zero代码贡献,则必须严格遵循此命名规范 参考文档 Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"路由规范","keywords":"","body":"路由规范 推荐脊柱式命名 小写单词、横杠(-)组合 见名知义 /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"coding-spec.html":{"url":"coding-spec.html","title":"编码规范","keywords":"","body":"编码规范 import 单行import不建议用圆括号包裹 按照官方包,NEW LINE,当前工程包,NEW LINE,第三方依赖包顺序引入 import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) 函数返回 对象避免非指针返回 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则 错误处理 有error必须处理,如果不能处理就必须抛出。 避免下划线(_)接收error 函数体编码 建议一个block结束空一行,如if、for等 func main (){ if x==1{ // do something } fmt.println(\"xxx\") } return前空一行 func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"dev-flow.html":{"url":"dev-flow.html","title":"开发流程","keywords":"","body":"开发流程 这里的开发流程和我们实际业务开发流程不是一个概念,这里的定义局限于go-zero的使用,即代码层面的开发细节。 开发流程 goctl环境准备[1] 数据库设计 业务开发 新建工程 创建服务目录 创建服务类型(api/rpc/rmq/job/script) 编写api、proto文件 代码生成 生成数据库访问层代码model 配置config,yaml变更 资源依赖填充(ServiceContext) 添加中间件 业务代码填充 错误处理 [!TIP] [1] goctl环境 开发工具 Visual Studio Code Goland(推荐) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"config-introduction.html":{"url":"config-introduction.html","title":"配置介绍","keywords":"","body":"配置介绍 在正式使用go-zero之前,让我们先来了解一下go-zero中不同服务类型的配置定义,看看配置中每个字段分别有什么作用,本节将包含以下小节: api配置 rpc配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-config.html":{"url":"api-config.html","title":"api配置","keywords":"","body":"api配置 api配置控制着api服务中的各种功能,包含但不限于服务监听地址,端口,环境配置,日志配置等,下面我们从一个简单的配置来看一下api中常用配置分别有什么作用。 配置说明 通过yaml配置我们会发现,有很多参数我们并没有与config对齐,这是因为config定义中,有很多都是带optional或者default 标签的,对于optional可选项,你可以根据自己需求判断是否需要设置,对于default标签,如果你觉得默认值就已经够了,可以不用设置, 一般default中的值基本不用修改,可以认为是最佳实践值。 Config type Config struct{ rest.RestConf // rest api配置 Auth struct { // jwt鉴权配置 AccessSecret string // jwt密钥 AccessExpire int64 // 有效期,单位:秒 } Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库 DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可 } CacheRedis cache.CacheConf // redis缓存 UserRpc zrpc.RpcClientConf // rpc client配置 } rest.RestConf api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制,对其展开我们可以看到: service.ServiceConf // service配置 Host string `json:\",default=0.0.0.0\"` // http监听ip,默认0.0.0.0 Port int // http监听端口,必填 CertFile string `json:\",optional\"` // https证书文件,可选 KeyFile string `json:\",optional\"` // https私钥文件,可选 Verbose bool `json:\",optional\"` // 是否打印详细http请求日志 MaxConns int `json:\",default=10000\"` // http同时可接受最大请求数(限流数),默认10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http可接受请求的最大ContentLength,默认1048576,被设置值必须在0到8388608之间 // milliseconds Timeout int64 `json:\",default=3000\"` // 超时时长控制,单位:毫秒,默认3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // cpu降载阈值,默认900,可允许设置范围0到1000 Signature SignatureConf `json:\",optional\"` // 签名配置 service.ServiceConf type ServiceConf struct { Name string // 服务名称 Log logx.LogConf // 日志配置 Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // 服务环境,dev-开发环境,test-测试环境,pre-预发环境,pro-正式环境 MetricsUrl string `json:\",optional\"` // 指标上报接口地址,该地址需要支持post json即可 Prometheus prometheus.Config `json:\",optional\"` // prometheus配置 } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // 服务名称 Mode string `json:\",default=console,options=console|file|volume\"` // 日志模式,console-输出到console,file-输出到当前服务器(容器)文件,,volume-输出docker挂载文件内 Path string `json:\",default=logs\"` // 日志存储路径 Level string `json:\",default=info,options=info|error|severe\"` // 日志级别 Compress bool `json:\",optional\"` // 是否开启gzip压缩 KeepDays int `json:\",optional\"` // 日志保留天数 StackCooldownMillis int `json:\",default=100\"` // 日志write间隔 } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus 监听host Port int `json:\",default=9101\"` // prometheus 监听端口 Path string `json:\",default=/metrics\"` // 上报地址 } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // 是否Strict模式,如果是则PrivateKeys必填 Expiry time.Duration `json:\",default=1h\"` // 有效期,默认1小时 PrivateKeys []PrivateKeyConf // 签名密钥相关配置 } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // 指纹配置 KeyFile string // 密钥配置 } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // 权重 } redis.RedisConf RedisConf struct { Host string // redis地址 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-config.html":{"url":"rpc-config.html","title":"rpc配置","keywords":"","body":"rpc配置 rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等,下面我们以一个常见的rpc服务配置来进行说明。 配置说明 Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // redis缓存配置,详情见api配置说明,这里不赘述 Mysql struct { // mysql数据库访问配置,详情见api配置说明,这里不赘述 DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // 服务配置,详情见api配置说明,这里不赘述 ListenOn string // rpc监听地址和端口,如:127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd相关配置 Auth bool `json:\",optional\"` // 是否开启Auth,如果是则Redis为必填 Redis redis.RedisKeyConf `json:\",optional\"` // Auth验证 StrictControl bool `json:\",optional\"` // 是否Strict模式,如果是则遇到错误是Auth失败,否则可以认为成功 // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // 超时控制,单位:毫秒 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` cpu降载阈值,默认900,可允许设置范围0到1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host数组 Key string // rpc注册key } redis.RedisKeyConf RedisConf struct { Host string // redis 主机 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // 验证key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-dev.html":{"url":"business-dev.html","title":"业务开发","keywords":"","body":"业务开发 本章节我们用一个简单的示例去演示一下go-zero中的一些基本功能。本节将包含以下小节: 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 演示工程下载 在正式进入后续文档叙述前,可以先留意一下这里的源码,后续我们会基于这份源码进行功能的递进式演示, 而不是完全从0开始,如果你从快速入门章节过来,这份源码结构对你来说不是问题。 点击这里下载演示工程基础源码 演示工程说明 场景 程序员小明需要借阅一本《西游记》,在没有线上图书管理系统的时候,他每天都要去图书馆前台咨询图书馆管理员, 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,明天再来看看吧。 过了一天,小明又来到图书馆,问: 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,你过两天再来看看吧。 就这样经过多次反复,小明也是徒劳无功,浪费大量时间在来回的路上,于是终于忍受不了落后的图书管理系统, 他决定自己亲手做一个图书查阅系统。 预期实现目标 用户登录 依靠现有学生系统数据进行登录 图书检索 根据图书关键字搜索图书,查询图书剩余数量。 系统分析 服务拆分 user api 提供用户登录协议 rpc 供search服务访问用户数据 search api 提供图书查询协议 [!TIP] 这个微小的图书借阅查询系统虽然小,从实际来讲不太符合业务场景,但是仅上面两个功能,已经满足我们对go-zero api/rpc的场景演示了, 后续为了满足更丰富的go-zero功能演示,会在文档中进行业务插入即相关功能描述。这里仅用一个场景进行引入。 注意:user中的sql语句请自行创建到db中去,更多准备工作见准备工作 添加一些预设的用户数据到数据库,便于后面使用,为了篇幅,演示工程不对插入数据这种操作做详细演示。 参考预设数据 INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-design.html":{"url":"service-design.html","title":"目录拆分","keywords":"","body":"目录拆分 目录拆分是指配合go-zero的最佳实践的目录拆分,这和微服务拆分有着关联,在团队内部最佳实践中, 我们按照业务横向拆分,将一个系统拆分成多个子系统,每个子系统应拥有独立的持久化存储,缓存系统。 如一个商城系统需要有用户系统(user),商品管理系统(product),订单系统(order),购物车系统(cart),结算中心系统(pay),售后系统(afterSale)等组成。 系统结构分析 在上文提到的商城系统中,每个系统在对外(http)提供服务的同时,也会提供数据给其他子系统进行数据访问的接口(rpc),因此每个子系统可以拆分成一个服务,而且对外提供了两种访问该系统的方式api和rpc,因此, 以上系统按照目录结构来拆分有如下结构: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc调用链建议 在设计系统时,尽量做到服务之间调用链是单向的,而非循环调用,例如:order服务调用了user服务,而user服务反过来也会调用order的服务, 当其中一个服务启动故障,就会相互影响,进入死循环,你order认为是user服务故障导致的,而user认为是order服务导致的,如果有大量服务存在相互调用链, 则需要考虑服务拆分是否合理。 常见服务类型的目录结构 在上述服务中,仅列举了api/rpc服务,除此之外,一个服务下还可能有其他更多服务类型,如rmq(消息处理系统),cron(定时任务系统),script(脚本)等, 因此一个服务下可能包含以下目录结构: user ├── api // http访问服务,业务需求实现 ├── cronjob // 定时任务,定时数据更新业务 ├── rmq // 消息处理系统:mq和dq,处理一些高并发和延时消息业务 ├── rpc // rpc服务,给其他子系统提供基础数据访问 └── script // 脚本,处理一些临时运营需求,临时数据修复 完整工程目录结构示例 mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script 猜你想看 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"model-gen.html":{"url":"model-gen.html","title":"model生成","keywords":"","body":"model生成 首先,下载好演示工程 后,我们以user的model来进行代码生成演示。 前言 model是服务访问持久化数据层的桥梁,业务的持久化数据常存在于mysql,mongo等数据库中,我们都知道,对于一个数据库的操作莫过于CURD, 而这些工作也会占用一部分时间来进行开发,我曾经在编写一个业务时写了40个model文件,根据不同业务需求的复杂性,平均每个model文件差不多需要 10分钟,对于40个文件来说,400分钟的工作时间,差不多一天的工作量,而goctl工具可以在10秒钟来完成这400分钟的工作。 准备工作 进入演示工程book,找到user/model下的user.sql文件,将其在你自己的数据库中执行建表。 代码生成(带缓存) 方式一(ddl) 进入service/user/model目录,执行命令 $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. 方式二(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource为数据库连接地址 方式三(intellij 插件) 在Goland中,右键user.sql,依次进入并点击New->Go Zero->Model Code即可生成,或者打开user.sql文件, 进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Mode Code即可 [!TIP] intellij插件生成需要安装goctl插件,详情见intellij插件 验证生成的model文件 查看tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go 更多 对于持久化数据,如果需要更灵活的数据库能力,包括事务能力,可以参考 Mysql 如果需要分布式事务的能力,可以参考 分布式事务支持 猜你想看 model命令及其原理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"api-coding.html":{"url":"api-coding.html","title":"api文件编写","keywords":"","body":"api文件编写 编写user.api文件 $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } 生成api服务 方式一 $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. 方式二 在 user.api 文件右键,依次点击进入 New->Go Zero->Api Code ,进入目标目录选择,即api源码的目标存放目录,默认为user.api所在目录,选择好目录后点击OK即可。 方式三 打开user.api,进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Api Code,同样进入目录选择弹窗,选择好目录后点击OK即可。 猜你想看 api语法 goctl api命令 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"business-coding.html":{"url":"business-coding.html","title":"业务编码","keywords":"","body":"业务编码 前面一节,我们已经根据初步需求编写了user.api来描述user服务对外提供哪些服务访问,在本节我们接着前面的步伐, 通过业务编码来讲述go-zero怎么在实际业务中使用。 添加Mysql配置 $ vim service/user/api/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/core/stores/cache\" ) type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } 完善yaml配置 $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 更多配置信息,请参考api配置介绍 完善服务依赖 $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } 填充登录逻辑 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"用户密码不正确\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] 上述代码中 [start]-[end]的代码实现见jwt鉴权章节 猜你想看 api语法 goctl api命令 api目录结构介绍 jwt鉴权 api配置介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"jwt.html":{"url":"jwt.html","title":"jwt鉴权","keywords":"","body":"jwt鉴权 概述 JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。 什么时候应该使用JWT 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。 信息交换:JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。 为什么要使用JSON Web令牌 由于JSON不如XML冗长,因此在编码时JSON的大小也较小,从而使JWT比SAML更为紧凑。这使得JWT是在HTML和HTTP环境中传递的不错的选择。 在安全方面,只能使用HMAC算法由共享机密对SWT进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公用/专用密钥对进行签名。与签署JSON的简单性相比,使用XML Digital Signature签署XML而不引入模糊的安全漏洞是非常困难的。 JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象的映射。与SAML断言相比,这使使用JWT更加容易。 关于用法,JWT是在Internet规模上使用的。这突显了在多个平台(尤其是移动平台)上对JSON Web令牌进行客户端处理的简便性。 [!TIP] 以上内容全部来自jwt官网介绍 go-zero中怎么使用jwt jwt鉴权一般在api层使用,我们这次演示工程中分别在user api登录时生成jwt token,在search api查询图书时验证用户jwt token两步来实现。 user api生成jwt token 接着业务编码章节的内容,我们完善上一节遗留的getJwtToken方法,即生成jwt token逻辑 添加配置定义和yaml配置项 $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:生成jwt token的密钥,最简单的方式可以使用一个uuid值。 $AccessExpire:jwt token有效期,单位:秒 更多配置信息,请参考api配置介绍 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search api使用jwt token鉴权 编写search.api文件 $ vim service/search/api/search.api type ( SearchReq { // 图书名称 Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth:开启jwt鉴权 如果路由需要jwt鉴权,则需要在service上方声明此语法标志,如上文中的/search/do 不需要jwt鉴权的路由就无需声明,如上文中/search/ping 更多语法请阅读api语法介绍 生成代码 前面已经描述过有三种方式去生成代码,这里就不赘述了。 添加yaml配置项 $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 这里修改一下端口,避免和user api端口8888冲突 验证 jwt token 启动user api服务,登录 $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'Content-Type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' 如果是在Windows的CMD里运行,命令格式如下: curl -i -X POST http://127.0.0.1:8888/user/login -H \"Content-Type: application/json\" -d \"{ \\\"username\\\":\\\"666\\\", \\\"password\\\":\\\"123456\\\" }\" 访问结果: HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"小明\",\"gender\":\"男\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} 启动search api服务,调用/search/do验证jwt鉴权是否通过 $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... 我们先不传jwt token,看看结果 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 很明显,jwt鉴权失败了,返回401的statusCode,接下来我们带一下jwt token(即用户登录返回的accessToken) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] 服务启动错误,请查看常见错误处理 至此,jwt从生成到使用就演示完成了,jwt token的鉴权是go-zero内部已经封装了,你只需在api文件中定义服务时简单的声明一下即可。 获取jwt token中携带的信息 go-zero从jwt token解析后会将用户生成token时传入的kv原封不动的放在http.Request的Context中,因此我们可以通过Context就可以拿到你想要的值 $ vim /service/search/api/internal/logic/searchlogic.go 添加一个log来输出从jwt解析出来的userId。 func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } 运行结果 {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} 猜你想看 jwt介绍 api配置介绍 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"middleware.html":{"url":"middleware.html","title":"中间件使用","keywords":"","body":"中间件使用 在上一节,我们演示了怎么使用jwt鉴权,相信你已经掌握了对jwt的基本使用,本节我们来看一下api服务中间件怎么使用。 中间件分类 在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,其和jwt类似,没有放在jwt:xxx下的路由不会使用中间件功能, 而全局中间件的服务范围则是整个服务。 中间件使用 这里以search服务为例来演示中间件的使用 路由中间件 重新编写search.api文件,添加middleware声明 $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // 路由中间件声明 ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } 重新生成api代码 $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. 生成完后会在internal目录下多一个middleware的目录,这里即中间件文件,后续中间件的实现逻辑也在这里编写。 完善资源依赖ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } 编写中间件逻辑 这里仅添加一行日志,内容example middle,如果服务运行输出example middle则代表中间件使用起来了。 $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } 启动服务验证 {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} 全局中间件 通过rest.Server提供的Use方法即可 func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // 全局中间件 server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} 在中间件里调用其它服务 通过闭包的方式把其它服务传递给中间件,示例如下: // 模拟的其它服务 type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // 常规中间件 func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // 调用其它服务的中间件 func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } 完整代码参考:https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"rpc-call.html":{"url":"rpc-call.html","title":"rpc服务编写与调用","keywords":"","body":"rpc编写与调用 在一个大的系统中,多个子系统(服务)间必然存在数据传递,有数据传递就需要通信方式,你可以选择最简单的http进行通信,也可以选择rpc服务进行通信, 在go-zero,我们使用zrpc来进行服务间的通信,zrpc是基于grpc。 场景 在前面我们完善了对用户进行登录,用户查询图书等接口协议,但是用户在查询图书时没有做任何用户校验,如果当前用户是一个不存在的用户则我们不允许其查阅图书信息, 从上文信息我们可以得知,需要user服务提供一个方法来获取用户信息供search服务使用,因此我们就需要创建一个user rpc服务,并提供一个getUser方法。 rpc服务编写 编译proto文件 $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } 生成rpc服务代码$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] 如果安装的 protoc-gen-go 版大于1.4.0, proto文件建议加上go_package 添加配置及完善yaml配置项 $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 $etcdHost: etcd连接地址,格式:ip:port,如: 127.0.0.1:2379 更多配置信息,请参考rpc配置介绍 添加资源依赖 $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } 添加rpc逻辑 $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } 使用rpc 接下来我们在search服务中调用user rpc 添加UserRpc配置及yaml配置项 $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 $etcdHost: etcd连接地址 etcd中的Key必须要和user rpc服务配置中Key一致 添加依赖 $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 补充逻辑 $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // 使用user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } 启动并验证服务 启动etcd、redis、mysql 启动user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml 验证服务 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"西游记\",\"count\":100} 猜你想看 rpc配置 rpc服务目录 goctl rpc命令 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error-handle.html":{"url":"error-handle.html","title":"错误处理","keywords":"","body":"错误处理 错误的处理是一个服务必不可缺的环节。在平时的业务开发中,我们可以认为http状态码不为2xx系列的,都可以认为是http请求错误, 并伴随响应的错误信息,但这些错误信息都是以plain text形式返回的。除此之外,我在业务中还会定义一些业务性错误,常用做法都是通过 code、msg 两个字段来进行业务处理结果描述,并且希望能够以json响应体来进行响应。 业务错误响应格式 业务处理正常 { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } 业务处理异常 { \"code\": 10001, \"msg\": \"参数错误\" } user api之login 在之前,我们在登录逻辑中处理用户名不存在时,直接返回来一个error。我们来登录并传递一个不存在的用户名看看效果。 curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 用户名不存在 接下来我们将其以json格式进行返回 自定义错误 首先在common中添加一个baseerror.go文件,并填入代码 $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } 将登录逻辑中错误用CodeError自定义错误替换 if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"用户密码不正确\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil 开启自定义错误 $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // 自定义错误 httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } 重启服务验证 $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"用户名不存在\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD 在软件工程中,CI/CD或CICD通常指的是持续集成和持续交付或持续部署的组合实践。 ——引自维基百科 CI可以做什么? 现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为“合并日”),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。 持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或“主干”中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。 ——引自《CI/CD是什么?如何理解持续集成、持续交付和持续部署》 从概念上来看,CI/CD包含部署过程,我们这里将部署(CD)单独放在一节服务部署, 本节就以gitlab来做简单的CI(Run Unit Test)演示。 gitlab CI Gitlab CI/CD是Gitlab内置的软件开发工具,提供 持续集成(CI) 持续交付(CD) 持续部署(CD) 准备工作 gitlab安装 git安装 gitlab runner安装 开启gitlab CI 上传代码 在gitlab新建一个仓库go-zero-demo 将本地代码上传到go-zero-demo仓库 在项目根目录下创建.gitlab-ci.yaml文件,通过此文件可以创建一个pipeline,其会在代码仓库中有内容变更时运行,pipeline由一个或多个按照顺序运行, 每个阶段可以包含一个或者多个并行运行的job。 添加CI内容(仅供参考) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] 以上CI为简单的演示,详细的gitlab CI请参考gitlab官方文档进行更丰富的CI集成。 参考文档 CI/CD 维基百科 CI/CD是什么?如何理解持续集成、持续交付和持续部署 Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-deployment.html":{"url":"service-deployment.html","title":"服务部署","keywords":"","body":"服务部署 本节通过jenkins来进行简单的服务部署到k8s演示。 准备工作 k8s集群安装 gitlab环境安装 jenkins环境安装 redis&mysql&nginx&etcd安装 goctl安装 [!TIP] goctl确保k8s每个node节点上都有 以上环境安装请自行google,这里不做篇幅介绍。 服务部署 1、gitlab代码仓库相关准备 1.1、添加SSH Key 进入gitlab,点击用户中心,找到Settings,在左侧找到SSH Keystab 1、在jenkins所在机器上查看公钥 $ cat ~/.ssh/id_rsa.pub 2、如果没有,则需要生成,如果存在,请跳转到第3步 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 完成生成后,重复第一步操作 3、将公钥添加到gitlab中 1.2、上传代码到gitlab仓库 新建工程go-zero-demo并上传代码,这里不做细节描述。 2、jenkins 2.1、添加凭据 查看jenkins所在机器的私钥,与前面gitlab公钥对应 $ cat id_rsa 进入jenkins,依次点击Manage Jenkins-> Manage Credentials 进入全局凭据页面,添加凭据,Username是一个标识,后面添加pipeline你知道这个标识是代表gitlab的凭据就行,Private Key`即上面获取的私钥 2.2、 添加全局变量 进入Manage Jenkins->Configure System,滑动到全局属性条目,添加docker私有仓库相关信息,如图为docker用户名、docker用户密码、docker私有仓库地址 [!TIP] docker_user 修改为你的docker用户名 docker_pass 修改为你的docker用户密码 docker_server 修改为你的docker服务器地址 这里我使用的私有仓库,如果没有云厂商提供的私有仓库使用,可以自行搭建一个私有仓库,这里就不赘述了,大家自行google。 2.3、配置git 进入Manage Jenkins->Global Tool Configureation,找到Git条目,填写jenkins所在机器git可执行文件所在path,如果没有的话,需要在jenkins插件管理中下载Git插件。 2.4、 添加一个Pipeline pipeline用于构建项目,从gitlab拉取代码->生成Dockerfile->部署到k8s均在这个步骤去做,这里是演示环境,为了保证部署流程顺利, 需要将jenkins安装在和k8s集群的其中过一个节点所在机器上,我这里安装在master上的。 获取凭据id 进入凭据页面,找到Username为gitlab的凭据id 进入jenkins首页,点击新建Item,名称为user 查看项目git地址 添加服务类型Choice Parameter,在General中勾选This project is parameterized,点击添加参数选择Choice Parameter,按照图中添加选择的值常量(api、rpc)及接收值的变量(type),后续在Pipeline script中会用到。 配置user,在user配置页面,向下滑动找到Pipeline script,填写脚本内容 pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: '选择需要构建的分支' } stages { stage('服务信息') { steps { sh 'echo 分支:$branch' sh 'echo 构建服务类型:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('获取commit_id') { steps { echo '获取commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl版本检测') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo 镜像名称:${image}' sh 'docker build -t ${image} .' } } stage('上传到镜像仓库') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('部署到k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId}要替换为你的具体凭据值,即【添加凭据】模块中的一串字符串,${gitUrl}需要替换为你代码的git仓库地址,其他的${xxx}形式的变量无需修改,保持原样即可。 port.sh参考内容如下 case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac 其中dockerlogin.sh内容 #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker登录用户名 $docker-pass: docker登录用户密码 $docker-server: docker私有地址 查看pipeline 查看k8s服务 猜你想看 goctl安装 k8s介绍 docker介绍 jenkins安装 jenkins pipeline nginx文档介绍 etcd文档说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"log-collection.html":{"url":"log-collection.html","title":"日志收集","keywords":"","body":"日志收集 为了保证业务稳定运行,预测服务不健康风险,日志的收集可以帮助我们很好的观察当前服务的健康状况, 在传统业务开发中,机器部署还不是很多时,我们一般都是直接登录服务器进行日志查看、调试,但随着业务的增大,服务的不断拆分, 服务的维护成本也会随之变得越来越复杂,在分布式系统中,服务器机子增多,服务分布在不同的服务器上,当遇到问题时, 我们不能使用传统做法,登录到服务器进行日志排查和调试,这个复杂度可想而知。 [!TIP] 如果是一个简单的单体服务系统或者服务过于小不建议直接使用,否则会适得其反。 准备工作 kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash filebeat配置 $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # 开启json解析 json.keys_under_root: true json.add_error_key: true # 日志文件路径 paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # 定义kafka topic field fields: log_topic: log-collection # 输出到kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx为filebeat.yaml所在路径 go-stash配置 新建config.yaml文件 添加配置内容 $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC 启动服务(按顺序启动) 启动kafka 启动elasticsearch 启动kibana 启动go-stash 启动filebeat 启动order-api服务及其依赖服务(go-zero-demo工程中的order-api服务) 访问kibana 进入127.0.0.1:5601 [!TIP] 这里仅演示收集服务中通过logx产生的日志,nginx中日志收集同理。 参考文档 kafka elasticsearch kibana filebeat go-stash filebeat配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"trace.html":{"url":"trace.html","title":"链路追踪","keywords":"","body":"go-zero链路追踪 序言 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http 。而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪。 追踪的方法就是在一个请求开始时生成一个自己的 spanID ,随着整个请求链路传下去。我们则通过这个 spanID 查看整个链路的情况和性能问题。 下面来看看 go-zero 的链路实现。 代码结构 spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 概念 SpanContext 在介绍 span 之前,先引入 context 。SpanContext 保存了分布式追踪的上下文信息,包括 Trace id,Span id 以及其它需要传递到下游的内容。OpenTracing 的实现需要将 SpanContext 通过某种协议 进行传递,以将不同进程中的 Span 关联到同一个 Trace 上。对于 HTTP 请求来说,SpanContext 一般是采用 HTTP header 进行传递的。 下面是 go-zero 默认实现的 spanContext type spanContext struct { traceId string // TraceID 表示tracer的全局唯一ID spanId string // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一 } 同时开发者也可以实现 SpanContext 提供的接口方法,实现自己的上下文信息传递: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // 自定义操作TraceId,SpanId } Span 一个 REST 调用或者数据库操作等,都可以作为一个 span 。 span 是分布式追踪的最小跟踪单位,一个 Trace 由多段 Span 组成。追踪信息包含如下信息: type Span struct { ctx spanContext // 传递的上下文 serviceName string // 服务名 operationName string // 操作 startTime time.Time // 开始时间戳 flag string // 标记开启trace是 server 还是 client children int // 本 span fork出来的 childsnums } 从 span 的定义结构来看:在微服务中, 这就是一个完整的子调用过程,有调用开始 startTime ,有标记自己唯一属性的上下文结构 spanContext 以及 fork 的子节点数。 实例应用 在 go-zero 中http,rpc中已经作为内置中间件集成。我们以 http ,rpc 中,看看 tracing 是怎么使用的: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } 将 header -> carrier,获取 header 中的traceId等信息 开启一个新的 span,并把「traceId,spanId」封装在context中 从上述的 carrier「也就是header」获取traceId,spanId 看header中是否设置 如果没有设置,则随机生成返回 从 request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回 从上述的 context,拷贝一份到当前的 request 这样就实现了 span 的信息随着 request 传递到下游服务。 RPC 在 rpc 中存在 client, server ,所以从 tracing 上也有 clientTracing, serverTracing 。 serveTracing 的逻辑基本与 http 的一致,来看看 clientTracing 是怎么使用的? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** 将 pair 中的data以map的形式加入 ctx ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } 获取上游带下来的 span 上下文信息 从获取的 span 中创建新的 ctx,span「继承父span的traceId」 将生成 span 的data加入ctx,传递到下一个中间件,流至下游 总结 go-zero 通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。开发者可以通过 ELK 工具追踪 traceID ,看到整个调用链。 同时 go-zero 并没有提供整套 trace 链路方案,开发者可以封装 go-zero 已有的 span 结构,做自己的上报系统,接入 jaeger, zipkin 等链路追踪工具。 参考 go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"service-monitor.html":{"url":"service-monitor.html","title":"服务监控","keywords":"","body":"服务监控 在微服务治理中,服务监控也是非常重要的一个环节,监控一个服务是否正常工作,需要从多维度进行,如: mysql指标 mongo指标 redis指标 请求日志 服务指标统计 服务健康检测 ... 监控的工作非常大,本节仅以其中的服务指标监控作为例子进行说明。 基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 $ prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl goctl是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有: api服务生成 rpc服务生成 model代码生成 模板管理 本节将包含以下内容: 自动补全设置 命令大全 api命令 rpc命令 model命令 plugin命令 其他命令 goctl 读音 很多人会把 goctl 读作 go-C-T-L,这种是错误的念法,应参照 go control 读做 ɡō kənˈtrōl。 查看版本信息 $ goctl -v 如果安装了goctl则会输出以下格式的文本信息: goctl version ${version} ${os}/${arch} 例如输出: goctl version 1.1.5 darwin/amd64 版本号说明 version:goctl 版本号 os:当前操作系统名称 arch: 当前系统架构名称 安装 goctl 方式一(go get) # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 通过此命令可以将goctl工具安装到 $GOPATH/bin 目录下 方式二 (fork and build) 从 go-zero代码仓库 git@github.com:zeromicro/go-zero.git 拉取一份源码,进入 tools/goctl/目录下编译一下 goctl 文件,然后将其添加到环境变量中。 安装完成后执行goctl -v,如果输出版本信息则代表安装成功,例如: $ goctl -v goctl version 1.1.4 darwin/amd64 常见问题 command not found: goctl 请确保goctl已经安装,或者goctl是否已经正确添加到当前shell的环境变量中。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"自动补全设置","keywords":"","body":"goctl自动补全 goctl 自动补全仅支持 unix-like 操作系统 用法 $ goctl completion -h NAME: goctl completion - generation completion script, it only works for unix-like OS USAGE: goctl completion [command options] [arguments...] OPTIONS: --name value, -n value the filename of auto complete script, default is [goctl_autocomplete] 生成自动补全文件 $ goctl completion generation auto completion success! executes the following script to setting shell: echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell 配置 zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc 演示效果 使用 tab 键出现自动补全提示 $ goctl api -- generate api related files bug -- report a bug completion -- generation completion script, it only works for unix-like OS docker -- generate Dockerfile help h -- Shows a list of commands or help for one command kube -- generate kubernetes files migrate -- migrate from tal-tech to zeromicro model -- generate model code rpc -- generate rpc code template -- template operation upgrade -- upgrade goctl to latest version Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"命令大全","keywords":"","body":"goctl命令大全 goctl bug (报告一个错误) upgrade (将goctl升级到最新版本) env (检查或编辑goctl环境) --write, -w: 编辑goctl环境 check (检测goctl环境和依赖性工具) --force, -f: 默许安装不存在的依赖项 --install, -i: 如果没有找到,就安装依赖工具 migrate (从tal-tech迁移到zeromicro) --verbose, -v: verbose可以实现额外的日志记录 --version: 要迁移的github.com/zeromicro/go-zero的目标版本。 api (生成api相关文件) --branch:远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 -o:输出api文件 new (快速创建api服务) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (格式化api文件) --declare:用于跳过检查已经声明的api类型 --dir: 格式目标目录 --iu: 忽略更新 --stdin:使用stdin输入api文件内容,按 \"ctrl + d \"发送EOF。 validate (验证api文件) --api: 验证目标api文件 doc (生成文档文件) --dir: 目标目录 --o: 输出markdown目录 go (提供的api生成go文件) --api: api文件 --branch: 远程 repo 的分支,它与 --remote 一起工作。 --dir: 目标目录 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (为api文件中提供的api生成java文件) --api: api文件 --dir: 目标目录 ts (为api文件中提供的api生成ts文件) --api: api文件 --caller: 网络api调用者 --dir: 目标目录 --unwrap: 解除webapi调用器的包装,以便导入 --webapi: web api文件的路径 dart (为api文件中提供的api生成dart文件) --api: api文件 --dir: 目标目录 --hostname: 服务器的主机名 --legacy: 用于flutter v1的传统生成器 kt (为提供的api文件生成kotlin代码) --api: api文件 --dir: 目标目录 --pkg: 定义kotlin文件的包名 plugin (自定义文件生成器) --api: api文件 --dir: 目标目录 --plugin, -p: 插件文件 --style: 文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (生成Docker文件) --branch:远程版本库的分支,它与--remote一起工作。 --go:包含主函数的文件 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --port:要公开的端口,默认为无(默认:0)。 --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --scratch:使用scratch作为基础docker镜像 --tz:容器的时区(默认:亚洲/上海) --version:goctl builder golang镜像的版本。 kube (生成kubernetes文件) deploy (生成部署yaml文件) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --image:部署的docker镜像 --limitCpu:部署的cpu上限(默认为1000)。 --limitMem: 部署的内存上限(默认为1024)。 --maxReplicas: 部署的最大复制数(默认为10)。 --minReplicas: 部署的最小复制量(默认为3)。 --name:部署的名称 --namespace:部署的命名空间 --nodePort: 要公开的部署的nodePort(默认为0)。 --port: 要在pod上监听的部署的端口(默认值:0) --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote有更高的优先级。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --replicas:要部署的副本数量(默认:3个)。 --requestCpu:要部署的请求cpu(默认为500)。 --requestMem: 要部署的请求内存(默认为512)。 --revisions: 限制修订历史的数量(默认为5)。 --secret: 从注册表中提取镜像的秘密。 --serviceAccount:部署的ServiceAccount。 -o: 输出的yaml文件 rpc (生成rpc代码) new (生成rpc演示服务) --branch: 远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 --idea:命令执行环境是否来自idea插件。[可选] --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (生成proto模板) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl主路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --out, -o: proto的目标路径 --remote:模板的远程git repo,--home和--remote不能同时设置,如果有的话,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 protoc (生成grpc代码) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl主路径 --remote: 模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out:zrpc的输出目录 model (生成model代码) mysql (生成mysql模型) ddl (从ddl生成mysql模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --database, --db:数据库的名称 [可选] - --dir, -d: 目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --src, -s:ddl的路径或路径globbing模式 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c: 使用缓存生成代码 [可选] - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t:数据库中的表或表球化模式 --url:数据库的数据源,如 \"root:password@tcp(127.0.0.1:3306)/database\" pg (生成postgresql模型) datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --schema, -s:表的模式,默认为[public] --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: 数据库中的表或表球化模式 --url:数据库的数据源,如 \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (生成mongo模型) --branch:远程repo的分支,它与--remote一起工作。 --cache, -c: 使用缓存生成代码 [可选] --dir, -d:目标目录 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t:指定的模型类型名称 template (模板操作) init (初始化所有模板(强制更新)) --home: 模板的goctl主路径 clean (清理所有缓存的模板) --home: 模板的goctl主路径 update (将目标类别的模板更新为最新的) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] --home: 模板的goctl主页路径 revert (将目标模板恢复到最新版本) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] 。 --home:模板的goctl主路径 --name, -n: 模板的目标文件名 completion (生成自动补全脚本,它只适用于类unix操作系统) --name, -n:自动完成脚本的文件名,默认为[goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-api.html":{"url":"goctl-api.html","title":"api命令","keywords":"","body":"api命令 goctl api是goctl中的核心模块之一,其可以通过.api文件一键快速生成一个api服务,如果仅仅是启动一个go-zero的api演示项目, 你甚至都不用编码,就可以完成一个api服务开发及正常运行。在传统的api项目中,我们要创建各级目录,编写结构体, 定义路由,添加logic文件,这一系列操作,如果按照一条协议的业务需求计算,整个编码下来大概需要5~6分钟才能真正进入业务逻辑的编写, 这还不考虑编写过程中可能产生的各种错误,而随着服务的增多,随着协议的增多,这部分准备工作的时间将成正比上升, 而goctl api则可以完全替代你去做这一部分工作,不管你的协议要定多少个,最终来说,只需要花费10秒不到即可完成。 [!TIP] 其中的结构体编写,路由定义用api进行替代,因此总的来说,省去的是你创建文件夹、添加各种文件及资源依赖的过程的时间。 api命令说明 $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help 从上文中可以看到,根据功能的不同,api包含了很多的自命令和flag,我们这里重点说明一下 go子命令,其功能是生成golang api服务,我们通过goctl api go -h看一下使用帮助: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir 代码输出目录 --api 指定api源文件 --style 指定生成代码文件的文件名称风格,详情见文件名称命名style说明 使用示例 $ goctl api go -api user.api -dir . -style gozero 猜你想看 api语法 api目录 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"rpc命令","keywords":"","body":"rpc命令 Goctl Rpc是goctl脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。 特性 简单易用 快速提升开发效率 出错率低 贴近protoc 快速开始 方式一:快速生成greet服务 通过命令 goctl rpc new ${servieName}生成 如生成greet rpc服务: goctl rpc new greet 执行后代码结构如下: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] 新版本目录详见 rpc目录 方式二:通过指定proto生成rpc服务 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\"./user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } 生成rpc服务代码 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. 准备工作 安装了go环境 安装了protoc & protoc-gen-go,并且已经设置环境变量 更多问题请见 注意事项 用法 rpc服务生成用法 goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 参数说明 --zrpc_out 可选,默认为proto文件所在目录,生成代码的目标目录 --style 可选,输出目录的文件命名风格,详情见https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md --home 可选,指定模板路径 --remote 可选,指定模板远程仓库 --branch 可选,指定模板远程仓库分支,与 --remote 配合使用 你可以理解为 zrpc 代码生成是用 goctl rpc $protoc_command --zrpc_out=${output} 模板,如原来生成 grpc 代码指令为 $ protoc user.proto --go_out=. --go-grpc_out=. 则生成 zrpc 代码指令就为 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out 与 --go-grpc_out 生成的最终目录必须一致 --go_out & --go-grpc_out 和 --zrpc_out 的生成的最终目录必须不为同一目录,否则pb.go和_grpc.pb.go就与main函数同级了,这是不允许的。 --go_out 与 --go-grpc_out 生产的目录会受 --go_opt 和 --grpc-go_opt 和proto源文件中 go_package值的影响,要想理解这里的生成逻辑,建议阅读 官方文文档:Go Generated Code 开发人员需要做什么 关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开发人员仅需要修改 服务中的配置文件编写(etc/xx.json、internal/config/config.go) 服务中业务逻辑编写(internal/logic/xxlogic.go) 服务中资源上下文的编写(internal/svc/servicecontext.go) 注意事项 proto暂不支持多文件同时生成 proto不支持外部依赖包引入,message不支持inline 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有 // Code generated by goctl. DO NOT EDIT! // Source: xxx.proto 的标识,请注意不要在里面写业务性代码;也不要将它写在业务性代码里面。 proto import 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。 proto示例: 错误import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request和return 不支持import } 正确import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// 支持import } message Response { base.Out out = 2;// 支持import } service Greet { rpc Ping(Request) returns(Response); } 猜你想看 rpc目录 rpc配置 rpc调用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-model.html":{"url":"goctl-model.html","title":"model命令","keywords":"","body":"model命令 goctl model 为go-zero下的工具模块中的组件之一,目前支持识别mysql ddl进行model层代码生成,通过命令行或者idea插件(即将支持)可以有选择地生成带redis cache或者不带redis cache的代码逻辑。 快速开始 通过ddl生成 $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c 执行上述命令后即可快速生成CURD代码。 model ├── usermodel.go ├── usermodel_gen.go └── vars.go 通过datasource生成 $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } 用法 $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help 生成规则 默认规则 我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,对于这两个字段生成insert、update时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。 ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 生成代码仅基本的CURD结构。 缓存 对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。 缓存会缓存哪些信息? 对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。 数据有更新(update)操作会清空缓存吗? 会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。 为什么不按照单索引字段生成updateByXxx和deleteByXxx的代码? 理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。 为什么不支持findPageLimit、findAll这种模式代码生成? 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 类型转换规则 mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"plugin命令","keywords":"","body":"plugin命令 goctl支持针对api自定义插件,那我怎么来自定义一个插件了?来看看下面最终怎么使用的一个例子。 $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . 上面这个命令可以分解成如下几步: goctl 解析api文件 goctl 将解析后的结构 ApiSpec 和参数传递给goctl-android可执行文件 goctl-android 根据 ApiSpec 结构体自定义生成逻辑。 此命令前面部分 goctl api plugin -p 是固定参数,goctl-android=\"android -package com.tal\" 是plugin参数,其中goctl-android是插件二进制文件,android -package com.tal是插件的自定义参数,-api user.api -dir .是goctl通用自定义参数。 怎么编写自定义插件? go-zero框架中包含了一个很简单的自定义插件 demo,代码如下: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() 这行代码作用是解析从goctl传递过来的数据,里面包含如下部分内容: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api:定义了api文件的结构数据 Style:可选参数,可以用来控制文件命名规范 Dir:工作目录 完整的基于plugin实现的android plugin演示项目 https://github.com/zeromicro/goctl-android 猜你想看 api目录 api语法 api配置 api命令介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goctl-other.html":{"url":"goctl-other.html","title":"其他命令","keywords":"","body":"其他命令 goctl docker goctl kube goctl docker goctl docker 可以极速生成一个 Dockerfile,帮助开发/运维人员加快部署节奏,降低部署复杂度。 准备工作 docker安装 Dockerfile 额外注意点 选择最简单的镜像:比如alpine,整个镜像5M左右 设置镜像时区RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai 多阶段构建 第一阶段构建出可执行文件,确保构建过程独立于宿主机 第二阶段将第一阶段的输出作为输入,构建出最终的极简镜像 Dockerfile编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 在 greet 项目下创建一个 hello 服务 $ goctl api new hello 文件结构如下: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go 在 hello 目录下一键生成 Dockerfile$ goctl docker -go hello.go Dockerfile 内容如下: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] 在 hello 目录下 build 镜像 $ docker build -t hello:v1 -f service/hello/Dockerfile . 查看镜像 hello v1 5455f2eaea6b 7 minutes ago 18.1MB 可以看出镜像大小约为18M。 启动服务$ docker run --rm -it -p 8888:8888 hello:v1 测试服务$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker总结 goctl 工具极大简化了 Dockerfile 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 goctl kube goctl kube提供了快速生成一个 k8s 部署文件的功能,可以加快开发/运维人员的部署进度,减少部署复杂度。 头疼编写 K8S 部署文件? K8S yaml 参数很多,需要边写边查? 保留回滚版本数怎么设? 如何探测启动成功,如何探活? 如何分配和限制资源? 如何设置时区?否则打印日志是 GMT 标准时间 如何暴露服务供其它服务调用? 如何根据 CPU 和内存使用率来配置水平伸缩? 首先,你需要知道有这些知识点,其次要把这些知识点都搞明白也不容易,再次,每次编写依然容易出错! 创建服务镜像 为了演示,这里我们以 redis:6-alpine 镜像为例。 完整 K8S 部署文件编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 一键生成 K8S 部署文件 $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 生成的 yaml 文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 部署服务,如果 adhoc namespace 不存在的话,请先通过 kubectl create namespace adhoc 创建 $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created 查看服务允许状态 $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s 测试服务$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube 总结 goctl 工具极大简化了 K8S yaml 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 猜你想看 准备工作 api目录 api语法 api配置 api命令介绍 docker介绍 k8s介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-manage.html":{"url":"template-manage.html","title":"模板管理","keywords":"","body":"模板管理 模板操作 自定义模板 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template-cmd.html":{"url":"template-cmd.html","title":"模板操作","keywords":"","body":"模板操作 模板(Template)是数据驱动生成的基础,所有的代码(rest api、rpc、model、docker、kube)生成都会依赖模板, 默认情况下,模板生成器会选择内存中的模板进行生成,而对于有模板修改需求的开发者来讲,则需要将模板进行落盘, 从而进行模板修改,在下次代码生成时会加载指定路径下的模板进行生成。 使用帮助 NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help 模板初始化 NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 清除模板 NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 回滚指定分类模板 NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template 回滚模板 NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home 指定模板存储路径 模板加载 在代码生成时可以通过--home来指定模板所在文件夹,目前已支持指定模板目录的命令有: goctl api go 详情可以通过goctl api go --help查看帮助 goctl docker 详情可以通过goctl docker --help查看帮助 goctl kube 详情可以通过goctl kube --help查看帮助 goctl rpc new 详情可以通过goctl rpc new --help查看帮助 goctl rpc proto 详情可以通过goctl rpc proto --help查看帮助 goctl model mysql ddl 详情可以通过goctl model mysql ddl --help查看帮助 goctl model mysql datasource 详情可以通过goctl model mysql datasource --help查看帮助 goctl model pg datasource 详情可以通过goctl model pg datasource --help查看帮助 goctl model mongo 详情可以通过goctl model mongo --help查看帮助 默认情况(在不指定--home)会从$HOME/.goctl目录下读取。 使用示例 初始化模板到指定$HOME/template目录下$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! 使用$HOME/template模板进行greet rpc生成$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"template.html":{"url":"template.html","title":"自定义模板","keywords":"","body":"模板修改 场景 实现统一格式的body响应,格式如下: { \"code\": 0, \"msg\": \"OK\", \"data\": {} // ① } ① 实际响应数据 [!TIP] go-zero生成的代码没有对其进行处理 准备工作 我们提前在module为greet的工程下的response包中写一个Response方法,目录树类似如下: greet ├── response │ └── response.go └── xxx... 代码如下 package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } 修改handler模板 $ vim ~/.goctl/api/handler.tpl 将模板替换为以下内容 package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① 替换为你真实的response包名,仅供参考 ② 自定义模板内容 [!TIP] 1.如果本地没有~/.goctl/api/handler.tpl文件,可以通过模板初始化命令goctl template init进行初始化 修改模板前后对比 修改前 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // 以下内容将被自定义模板替换 if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } 修改后 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } 修改模板前后响应体对比 修改前 { \"message\": \"Hello go-zero!\" } 修改后 { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } 总结 本文档仅对http相应为例讲述了自定义模板的流程,除此之外,自定义模板的场景还有: model 层添加kmq model 层生成待有效期option的model实例 http自定义相应格式 ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"extended-reading.html":{"url":"extended-reading.html","title":"扩展阅读","keywords":"","body":"扩展阅读 扩展阅读是对go-zero 中的最佳实现和组件的介绍, 因此会比较庞大,而此资源将会持续更新,也欢迎大家来进行文档贡献,本节将包含以下目录(按照文档更新时间排序): 快速构建高并发微服务 日志组件介绍 布隆过滤器 executors 流处理组件 fx go-zero mysql使用介绍 redis锁 periodlimit限流 令牌桶限流 时间轮介绍 熔断原理与实现 进程内缓存组件 collection.Cache 高效的关键词替换和敏感词过滤工具 服务自适应降载保护设计 文本序列化和反序列化 并发处理工具 MapReduce 基于prometheus的微服务指标监控 防止缓存击穿之进程内共享调用 DB缓存机制 zrpc 使用介绍 go-zero缓存设计之持久层缓存 go-zero缓存设计之业务层缓存 go-zero分布式定时任务 我是如何用go-zero 实现一个中台系统 流数据处理利器 10月3日线上交流问题汇总 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"logx.html":{"url":"logx.html","title":"日志组件介绍","keywords":"","body":"logx 使用示例 var c logx.LogConf // 从 yaml 文件中 初始化配置 conf.MustLoad(\"config.yaml\", &c) // logx 根据配置初始化 logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() 初始化 logx 有很多可以配置项,可以参考 logx.LogConf 中的定义。目前可以使用 logx.MustSetUp(c) 进行初始化配置,如果没有进行初始化配置,所有的配置将使用默认配置。 Level logx 支持的打印日志级别有: alert info error severe fatal slow stat 可以使用对应的方法打印出对应级别的日志。 同时为了方便调试,线上使用,可以动态调整日志打印级别,其中可以通过 logx.SetLevel(uint32) 进行级别设置,也可以通过配置初始化进行设置。目前支持的参数为: const ( // 打印所有级别的日志 InfoLevel = iota // 打印 errors, slows, stacks 日志 ErrorLevel // 仅打印 severe 级别日志 SevereLevel ) 日志模式 目前日志打印模式主要分为2种,一种文件输出,一种控制台输出。推荐方式,当采用 k8s,docker 等部署方式的时候,可以将日志输出到控制台,使用日志收集器收集导入至 es 进行日志分析。如果是直接部署方式,可以采用文件输出方式,logx 会自动在指定文件目录创建对应 5 个对应级别的的日志文件保存日志。 . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log 同时会按照自然日进行文件分割,当超过指定配置天数,会对日志文件进行自动删除,打包等操作。 禁用日志 如果不需要日志打印,可以使用 logx.Close() 关闭日志输出。注意,当禁用日志输出,将无法在次打开,具体可以参考 logx.RotateLogger 和 logx.DailyRotateRule 的实现。 关闭日志 因为 logx 采用异步进行日志输出,如果没有正常关闭日志,可能会造成部分日志丢失的情况。必须在程序退出的地方关闭日志输出: logx.Close() 框架中 rest 和 zrpc 等大部分地方已经做好了日志配置和关闭相关操作,用户可以不用关心。 同时注意,当关闭日志输出之后,将无法在次打印日志了。 推荐写法: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration 我们打印日志的时候可能需要打印耗时情况,可以使用 logx.WithDuration(time.Duration), 参考如下示例: startTime := timex.Now() // 数据库查询 rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } 会输出如下格式 {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} 这样就可以很容易统计出慢 sql 相关信息。 TraceLog tracingEntry 是为了链路追踪日志输出定制的。可以打印 context 中的 traceId 和 spanId 信息,配合我们的 rest 和 zrpc 很容易完成链路日志的相关打印。示例如下 logx.WithContext(context.Context).Info(\"This is info!\") SysLog 应用中可能有部分采用系统 log 进行日志打印,logx 同样封装方法,很容易将 log 相关的日志收集到 logx 中来。 logx.CollectSysLog() 日志配置相关 LogConf 定义日志系统所需的基本配置 完整定义如下: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode 定义了日志打印的方式。默认的模式是 console, 打印到控制台上面。 目前支持的模式如下: console 打印到控制台 file 打印到指定路径下的access.log, error.log, stat.log等文件里 volume 为了在k8s内打印到mount进来的存储上,因为多个pod可能会覆盖相同的文件,volume模式自动识别pod并按照pod分开写各自的日志文件 Path Path 定义了文件日志的输出路径,默认值为 logs。 Level Level 定义了日志打印级别,默认值为 info。 目前支持的级别如下: info error severe Compress Compress 定义了日志是否需要压缩,默认值为 false。在 Mode 为 file 模式下面,文件最后会进行打包压缩成 .gz 文件。 KeepDays KeepDays 定义日志最大保留天数,默认值为 0,表示不会删除旧的日志。在 Mode 为 file 模式下面,如果超过了最大保留天数,旧的日志文件将会被删除。 StackCooldownMillis StackCooldownMillis 定义了日志输出间隔,默认为 100 毫秒。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"bloom.html":{"url":"bloom.html","title":"布隆过滤器","keywords":"","body":"bloom go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 布隆过滤器bloom 在做服务器开发的时候,相信大家有听过布隆过滤器,可以判断某元素在不在集合里面,因为存在一定的误判和删除复杂问题,一般的使用场景是:防止缓存击穿(防止恶意攻击)、 垃圾邮箱过滤、cache digests 、模型检测器等、判断是否存在某行数据,用以减少对磁盘访问,提高服务的访问性能。 go-zero 提供的简单的缓存封装 bloom.bloom,简单使用方式如下 // 初始化 redisBitSet store := redis.New(\"redis 地址\", func(r *redis.Redis) { r.Type = redis.NodeType }) // 声明一个bitSet, key=\"test_key\"名且bits是1024位 bitSet := newRedisBitSet(store, \"test_key\", 1024) // 判断第0位bit存不存在 isSetBefore, err := bitSet.check([]uint{0}) // 对第512位设置为1 err = bitSet.set([]uint{512}) // 3600秒后过期 err = bitSet.expire(3600) // 删除该bitSet err = bitSet.del() bloom 简单介绍了最基本的redis bitset 的使用。下面是真正的bloom实现。 对元素hash 定位 // 对元素进行hash 14次(const maps=14),每次都在元素后追加byte(0-13),然后进行hash. // 将locations[0-13] 进行取模,最终返回locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i 向bloom里面add 元素 // 我们可以发现 add方法使用了getLocations和bitSet的set方法。 // 我们将元素进行hash成长度14的uint切片,然后进行set操作存到redis的bitSet里面。 func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } 检查bloom里面是否有某元素 // 我们可以发现 Exists方法使用了getLocations和bitSet的check方法 // 我们将元素进行hash成长度14的uint切片,然后进行bitSet的check验证,存在返回true,不存在或者check失败返回false func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } 本节主要介绍了go-zero框架中的 core.bloom 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors 在 go-zero 中,executors 充当任务池,做多任务缓冲,适用于做批量处理的任务。如:clickhouse 大批量 insert,sql batch insert。同时也可以在 go-queue 中看到 executors 【在 queue 里面使用的是 ChunkExecutor ,限定任务提交字节大小】。 所以当你存在以下需求,都可以使用这个组件: 批量提交任务 缓冲一部分任务,惰性提交 延迟任务提交 具体解释之前,先给一个大致的概览图: 接口设计 在 executors 包下,有如下几个 executor : Name Margin value bulkexecutor 达到 maxTasks 【最大任务数】 提交 chunkexecutor 达到 maxChunkSize【最大字节数】提交 periodicalexecutor basic executor delayexecutor 延迟执行传入的 fn() lessexecutor 你会看到除了有特殊功能的 delay,less ,其余 3 个都是 executor + container 的组合设计: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // 选项模式:在 go-zero 中多处出现。在多配置下,比较好的设计思路 // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute 真正做执行的函数] [maxTasks 执行临界点] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. 可以看出 bulkexecutor 底层依赖 periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } 而这个 container是个 interface: TaskContainer interface { // 把 task 加入 container AddTask(task interface{}) bool // 实际上是去执行传入的 execute func() Execute(tasks interface{}) // 达到临界值,移除 container 中全部的 task,通过 channel 传递到 execute func() 执行 RemoveAll() interface{} } 由此可见之间的依赖关系: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] 所以你想完成自己的 executor,可以实现 container 的这 3 个接口,再结合 periodicalexecutor 就行 所以回到👆那张图,我们的重点就放在 periodicalexecutor,看看它是怎么设计的? 如何使用 首先看看如何在业务中使用这个组件: 现有一个定时服务,每天固定时间去执行从 mysql 到 clickhouse 的数据同步: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } 初始化 bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() 是真正insert执行函数【需要开发者自己编写具体业务逻辑】 dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // 3s会自动刷一次container中task去执行 executors.WithBulkTasks(10240), // container最大task数。一般设为2的幂次 ) } [!TIP] 额外介绍一下:clickhouse 适合大批量的插入,因为 insert 速度很快,大批量 insert 更能充分利用 clickhouse 主体业务逻辑编写: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] 可能会疑惑为什么要 Flush(), Wait() ,后面会通过源码解析一下 使用上总体分为 3 步: Add():加入 task Flush():刷新 container 中的 task Wait():等待全部 task 执行完成 源码分析 [!TIP] 此处主要分析 periodicalexecutor,因为其他两个常用的 executor 都依赖它 初始化 func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander:传递 tasks 的 channel container:暂存 Add() 的 task confirmChan:阻塞 Add() ,在开始本次的 executeTasks() 会放开阻塞 ticker:定时器,防止 Add() 阻塞时,会有一个定时执行的机会,及时释放暂存的 task Add() 初始化完,在业务逻辑的第一步就是把 task 加入 executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask 将container中tasks pop, return if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } addAndCheck() 中 AddTask() 就是在控制最大 tasks 数,如果超过就执行 RemoveAll() ,将暂存 container 的 tasks pop,传递给 commander ,后面有 goroutine 循环读取,然后去执行 tasks。 backgroundFlush() 开启一个后台协程,对 container 中的 task,不断刷新: func (pe *PeriodicalExecutor) backgroundFlush() { // 封装 go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // 从channel拿到 []tasks case vals := pe.interval*idleRound { // 既没到maxTask,Flush() err,并且 last->now 时间过长,会再次触发 Flush() // 只有这置反,才会开启一个新的 backgroundFlush() 后台协程 pe.guarded = false // 再次刷新,防止漏掉 pe.Flush() return } } } }) } 总体两个过程: commander 接收到 RemoveAll() 传递来的 tasks,然后执行,并放开 Add() 的阻塞,得以继续 Add() ticker 到时间了,如果第一步没有执行,则自动 Flush() ,也会去做 task 的执行 Wait() 在 backgroundFlush() ,提到一个函数:enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } 这样列举就知道为什么之前在最后要带上 dts.insertExecutor.Wait(),当然要等待全部的 goroutine task 完成。 思考 在看源码中,思考了一些其他设计上的思路,大家是否也有类似的问题: 在分析 executors 中,会发现很多地方都有 lock [!TIP] go test 存在竞态,使用加锁来避免这种情况 在分析 confirmChan 时发现,confirmChan 在此次提交才出现,为什么会这么设计? 之前是:wg.Add(1) 是写在 executeTasks() ;现在是:先wg.Add(1),再放开 confirmChan 阻塞 如果 executor func 执行阻塞,Add task 还在进行,因为没有阻塞,可能很快执行到 Executor.Wait(),这时就会出现 wg.Wait() 在 wg.Add() 前执行,这会 panic 具体可以看最新版本的TestPeriodicalExecutor_WaitFast() ,不妨跑在此版本上,就可以重现 总结 剩余还有几个 executors 的分析,就留给大家去看看源码。 总之,整体设计上: 遵循面向接口设计 灵活使用 channel ,waitgroup 等并发工具 执行单元+存储单元的搭配使用 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"fx.html":{"url":"fx.html","title":"流处理组件 fx","keywords":"","body":"数据的流处理利器 流处理(Stream processing)是一种计算机编程范式,其允许给定一个数据序列(流处理数据源),一系列数据操作(函数)被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到Kafka,然后再由Job消费Kafaka写到elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx gozero是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce工具进行处理。 总结 本篇文章介绍了流处理的基本概念和gozero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mysql.html":{"url":"mysql.html","title":"go-zero mysql使用介绍","keywords":"","body":"mysql go-zero 提供更易于操作的 mysql API。 [!TIP] 但是 stores/mysql 定位不是一个 orm 框架,如果你需要通过 sql/scheme -> model/struct 逆向生成 model 层代码,可以使用「goctl model」,这个是极好的功能。 Feature 相比原生,提供对开发者更友好的 API 完成 queryField -> struct 的自动赋值 批量插入「bulkinserter」 自带熔断 API 经过若干个服务的不断考验 提供 partial assignment 特性,不强制 struct 的严格赋值 Connection 下面用一个例子简单说明一下如何创建一个 mysql 连接的 model: // 1. 快速连接一个 mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. 在 servicecontext 中调用,懂model上层的logic层调用 model.NewMysqlModel(heraMysql, tablename), // 3. model层 mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. 创建一个批量insert的 [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD 准备一个 User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` // 头像 UserName string `db:\"user_name\"` // 姓名 Sex int `db:\"sex\"` // 1男,2女 MobilePhone string `db:\"mobile_phone\"` // 手机号 } 其中 userBuilderQueryRows : go-zero 中提供 struct -> [field...] 的转化,开发者可以将此当成模版直接使用。 insert // 一个实际的insert model层操作 func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } 拼接 insertsql 将 insertsql 以及占位符对应的 struct field 传入 -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...) : args... 需对应 sql 中的占位符。不然会出现赋值异常的问题。 go-zero 将涉及 mysql 修改的操作统一抽象为 Exec() 。所以 insert/update/delete 操作本质上是一致的。其余两个操作,开发者按照上述 insert 流程尝试即可。 query 只需要传入 querysql 和 model 结构体,就可以获取到被赋值好的 model 。无需开发者手动赋值。 func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } 声明 model struct ,拼接 querysql conn.QueryRow(&model, querysql, args...) : args... 与 querysql 中的占位符对应。 [!WARNING] QueryRow() 中第一个参数需要传入 Ptr 「底层需要反射对 struct 进行赋值」 上述是查询一条记录,如果需要查询多条记录时,可以使用 conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } 与 QueryRow() 不同的地方在于: model 需要设置成 Slice ,因为是查询多行,需要对多个 model 赋值。但同时需要注意️:第一个参数需要传入 Ptr querypartial 从使用上,与上述的 QueryRow() 无异「这正体现了 go-zero 高度的抽象设计」。 区别: QueryRow() : len(querysql fields) == len(struct) ,且一一对应 QueryRowPartial() :len(querysql fields) numA:数据库字段数;numB:定义的 struct 属性数。 如果 numA ,但是你恰恰又需要统一多处的查询时「定义了多个 struct 返回不同的用途,恰恰都可以使用相同的 querysql 」,就可以使用 QueryRowPartial() 事务 要在事务中执行一系列操作,一般流程如下: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // 返回任何错误都会回滚事务 if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // 还可以继续执行 insert/update/delete 相关操作 return nil }) 如同上述例子,开发者只需将 事务 中的操作都包装在一个函数 func(session sqlx.Session) error {} 中即可,如果事务中的操作返回任何错误, Transact() 都会自动回滚事务。 分布式事务 go-zero 与 dtm 深度合作,原生的支持了分布式事务,详情参见 分布式事务支持 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis锁","keywords":"","body":"redis-lock redis lock 既然是锁,首先想到的一个作用就是:防重复点击,在一个时间点只有一个请求产生效果。 而既然是 redis,就得具有排他性,同时也具有锁的一些共性: 高性能 不能出现死锁 不能出现节点down掉后加锁失败 go-zero 中利用 redis set key nx 可以保证key不存在时写入成功,px 可以让key超时后自动删除「最坏情况也就是超时自动删除key,从而也不会出现死锁」 example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. 可选操作,设置 redislock 过期时间 redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"当前有其他用户正在进行操作,请稍后重试\") } defer func() { recover() // 3. 释放锁 redisLock.Release() }() 和你在使用 sync.Mutex 的方式时一致的。加锁解锁,执行你的业务操作。 获取锁 lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } 先介绍几个 redis 的命令选项,以下是为 set 命令增加的选项: ex seconds :设置key过期时间,单位s px milliseconds :设置key过期时间,单位毫秒 nx:key不存在时,设置key的值 xx:key存在时,才会去设置key的值 其中 lua script 涉及的入参: args 示例 含义 KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD 唯一标识:随机字符串 ARGV[2] 30000 设置锁的过期时间 然后来说说代码特性: Lua 脚本保证原子性「当然,把多个操作在 Redis 中实现成一个操作,也就是单命令操作」 使用了 set key value px milliseconds nx value 具有唯一性 加锁时首先判断 key 的 value 是否和之前设置的一致,一致则修改过期时间 释放锁 delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } 释放锁的时候只需要关注一点: 不能释放别人的锁,不能释放别人的锁,不能释放别人的锁 所以需要先 get(key) == value「key」,为 true 才会去 delete Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit限流","keywords":"","body":"periodlimit 不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性。即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪。 本文就来介绍一下 periodlimit 。 使用 const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero 采取 滑动窗口 计数的方式,计算一段时间内对同一个资源的访问次数,如果超过指定的 limit ,则拒绝访问。当然如果你是在一段时间内访问不同的资源,每一个资源访问量都不超过 limit ,此种情况是允许大量请求进来的。 而在一个分布式系统中,存在多个微服务提供服务。所以当瞬间的流量同时访问同一个资源,如何让计数器在分布式系统中正常计数? 同时在计算资源访问时,可能会涉及多个计算,如何保证计算的原子性? go-zero 借助 redis 的 incrby 做资源访问计数 采用 lua script 做整个窗口计算,保证计算的原子性 下面来看看 lua script 控制的几个关键属性: argument mean key[1] 访问资源的标示 ARGV[1] limit => 请求总数,超过则限速。可设置为 QPS ARGV[2] window大小 => 滑动窗口,用 ttl 模拟出滑动的效果 -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- 如果是第一次访问,设置过期时间 => TTL = window size -- 因为是只限制一段时间的访问次数 if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current 至于上述的 return code ,返回给调用方。由调用方来决定请求后续的操作: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit 下面这张图描述了请求进入的过程,以及请求触发 limit 时后续发生的情况: 后续处理 如果在服务某个时间点,请求大批量打进来,periodlimit 短期时间内达到 limit 阈值,而且设置的时间范围还远远没有到达。后续请求的处理就成为问题。 periodlimit 中并没有处理,而是返回 code 。把后续请求的处理交给了开发者自己处理。 如果不做处理,那就是简单的将请求拒绝 如果需要处理这些请求,开发者可以借助 mq 将请求缓冲,减缓请求的压力 采用 tokenlimit,允许暂时的流量冲击 所以下一篇我们就来聊聊 tokenlimit 总结 go-zero 中的 periodlimit 限流方案是基于 redis 计数器,通过调用 redis lua script ,保证计数过程的原子性,同时保证在分布式的情况下计数是正常的。但是这种方案存在缺点,因为它要记录时间窗口内的所有行为记录,如果这个量特别大的时候,内存消耗会变得非常严重。 参考 go-zero periodlimit 分布式服务限流实战,已经为你排好坑了 tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"令牌桶限流","keywords":"","body":"tokenlimit 本节将通过令牌桶限流(tokenlimit)来介绍其基本使用。 使用 const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit 从整体上令牌桶生产token逻辑如下: 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中; 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃; 当流量以速率v进入,从桶中以速率v取令牌,拿到令牌的流量通过,拿不到令牌流量不通过,执行熔断逻辑; go-zero 在两类限流器下都采取 lua script 的方式,依赖redis可以做到分布式限流,lua script同时可以做到对 token 生产读取操作的原子性。 下面来看看 lua script 控制的几个关键属性: argument mean ARGV[1] rate 「每秒生成几个令牌」 ARGV[2] burst 「令牌桶最大值」 ARGV[3] now_time「当前时间戳」 ARGV[4] get token nums 「开发者需要获取的token数」 KEYS[1] 表示资源的tokenkey KEYS[2] 表示刷新时间的key -- 返回是否可以活获得预期的token local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:需要填满 token_bucket 需要多久 local fill_time = capacity/rate -- 将填充时间向下取整 local ttl = math.floor(fill_time*2) -- 获取目前 token_bucket 中剩余 token 数 -- 如果是第一次进入,则设置 token_bucket 数量为 令牌桶最大值 local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- 上一次更新 token_bucket 的时间 local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- 通过当前时间与上一次更新时间的跨度,以及生产token的速率,计算出新的token数 -- 如果超过 max_burst,多余生产的token会被丢弃 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- 更新新的token数,以及更新时间 redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed 上述可以看出 lua script :只涉及对 token 操作,保证 token 生产合理和读取合理。 函数分析 从上述流程中看出: 有多重保障机制,保证限流一定会完成。 如果redis limiter失效,至少在进程内rate limiter兜底。 重试 redis limiter 机制保证尽可能地正常运行。 总结 go-zero 中的 tokenlimit 限流方案适用于瞬时流量冲击,现实请求场景并不以恒定的速率。令牌桶相当预请求,当真实的请求到达不至于瞬间被打垮。当流量冲击到一定程度,则才会按照预定速率进行消费。 但是生产token上,不能按照当时的流量情况作出动态调整,不够灵活,还可以进行进一步优化。此外可以参考Token bucket WIKI 中提到分层令牌桶,根据不同的流量带宽,分至不同排队中。 参考 go-zero tokenlimit Go-Redis 提供的分布式限流库 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"时间轮介绍","keywords":"","body":"TimingWheel 本文来介绍 go-zero 中 延迟操作。延迟操作,可以采用两个方案: Timer:定时器维护一个优先队列,到时间点执行,然后把需要执行的 task 存储在 map 中 collection 中的 timingWheel ,维护一个存放任务组的数组,每一个槽都维护一个存储task的双向链表。开始执行时,计时器每隔指定时间执行一个槽里面的tasks。 方案2把维护task从 优先队列 O(nlog(n)) 降到 双向链表 O(1),而执行task也只要轮询一个时间点的tasks O(N),不需要像优先队列,放入和删除元素 O(nlog(n))。 cache 中的 timingWheel 首先我们先来在 collection 的 cache 中关于 timingWheel 的使用: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel 这是 cache 初始化中也同时初始化 timingWheel 做key的过期处理,参数依次代表: interval:时间划分刻度 numSlots:时间槽 execute:时间点执行函数 在 cache 中执行函数则是 删除过期key,而这个过期则由 timingWheel 来控制推进时间。 接下来,就通过 cache 对 timingWheel 的使用来认识。 初始化 // 真正做初始化 func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // 单个时间格时间间隔 ticker: ticker, // 定时器,做时间推动,以interval为单位推进 slots: make([]*list.List, numSlots), // 时间轮 timers: NewSafeMap(), // 存储task{key, value}的map [执行execute所需要的参数] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // 执行函数 numSlots: numSlots, // 初始化 slots num setChannel: make(chan timingEntry), // 以下几个channel是做task传递的 moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // 把 slot 中存储的 list 全部准备好 tw.initSlots() // 开启异步协程,使用 channel 来做task通信和传递 go tw.run() return tw, nil } 以上比较直观展示 timingWheel 的 “时间轮”,后面会围绕这张图解释其中推进的细节。 go tw.run() 开一个协程做时间推动: func (tw *TimingWheel) run() { for { select { // 定时器做时间推动 -> scanAndRunTasks() case 可以看出,在初始化的时候就开始了 timer 执行,并以interval时间段转动,然后底层不停的获取来自 slot 中的 list 的task,交给 execute 执行。 Task Operation 紧接着就是设置 cache key : func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } 先看在 data map 中有没有存在这个key 存在,则更新 expire -> MoveTimer() 第一次设置key -> SetTimer() 所以对于 timingWheel 的使用上就清晰了,开发者根据需求可以 add 或是 update。 同时我们跟源码进去会发现:SetTimer() MoveTimer() 都是将task输送到channel,由 run() 中开启的协程不断取出 channel 的task操作。 SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() 由上面的调用链,有一个都会调用的函数:moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => 通过key获取 [positionEntry「pos, task」] val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay 延迟时间比一个时间格间隔还小,没有更小的刻度,说明任务应该立即执行 if task.delay interval,则通过 延迟时间delay 计算其出时间轮中的 new pos, circle pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // 记录前后的移动offset。为了后面过程重新入队 timer.item.diff = pos - timer.pos } else if circle > 0 { // 转移到下一层,将 circle 转换为 diff 一部分 circle-- timer.item.circle = circle // 因为是一个数组,要加上 numSlots [也就是相当于要走到下一层] timer.item.diff = tw.numSlots + pos - timer.pos } else { // 如果 offset 提前了,此时 task 也还在第一层 // 标记删除老的 task,并重新入队,等待被执行 timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } 以上过程有以下几种情况: delay :因为 针对改变的 delay: new >= old: newCircle > 0:计算diff,并将 circle 转换为 下一层,故diff + numslots 如果只是单纯延迟时间缩短,则将老的task标记删除,重新加入list,等待下一轮loop被execute Execute 之前在初始化中,run() 中定时器的不断推进,推进的过程主要就是把 list中的 task 传给执行的 execute func。我们从定时器的执行开始看: // 定时器 「每隔 interval 会执行一次」 func (tw *TimingWheel) onTick() { // 每次执行更新一下当前执行 tick 位置 tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // 获取此时 tick位置 中的存储task的双向链表 l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } 紧接着是如何去执行 execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // 存储目前需要执行的task{key, value} [execute所需要的参数,依次传递给execute执行] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // 标记删除,在 scan 中做真正的删除 「删除map的data」 if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // 当前执行点已经过期,但是同时不在第一层,所以当前层即然已经完成了,就会降到下一层 // 但是并没有修改 pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // 因为之前已经标注了diff,需要再进入队列 next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // 以上的情况都是不能执行的情况,能够执行的会被加入tasks中 tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks,然后把每个 task->execute 执行即可 tw.runTasks(tasks) } 具体的分支情况在注释中说明了,在看的时候可以和前面的 moveTask() 结合起来,其中 circle 下降,diff 的计算是关联两个函数的重点。 至于 diff 计算就涉及到 pos, circle 的计算: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } 上面的过程可以简化成下面: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots 总结 timingWheel 靠定时器推动,时间前进的同时会取出当前时间格中 list「双向链表」的task,传递到 execute 中执行。 而时间分隔上,时间轮有 circle 分层,这样就可以不断复用原有的 numSlots ,因为定时器在不断 loop,而执行可以把上层的 slot 下降到下层,在不断 loop 中就可以执行到上层的task。 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"eco.html":{"url":"eco.html","title":"go-zero 生态","keywords":"","body":"go-zero 生态 工具中心 intellij插件 vscode插件 分布式事务支持 插件中心 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tool-center.html":{"url":"tool-center.html","title":"工具中心","keywords":"","body":"工具中心 在go-zero中,提供了很多提高工程效率的工具,如api,rpc生成,在此基础之上,api文件的编写就显得那么的无力, 因为缺少了高亮,代码提示,模板生成等,本节将带你了解go-zero是怎么解决这些难题的,本节包含以下小节: intellij插件 vscode插件 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"intellij.html":{"url":"intellij.html","title":"intellij插件","keywords":"","body":"intellij插件 Go-Zero Plugin 介绍 一款支持go-zero api语言结构语法高亮、检测以及api、rpc、model快捷生成的插件工具。 idea版本要求 IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ 版本特性 api语法高亮 api语法、语义检测 struct、route、handler重复定义检测 type跳转到类型声明位置 上下文菜单中支持api、rpc、mode相关menu选项 代码格式化(option+command+L) 代码提示 安装方式 方式一 在github的release中找到最新的zip包,下载本地安装即可。(无需解压) 方式二 在plugin商店中,搜索Goctl安装即可 预览 新建 Api(Proto) file 在工程区域目标文件夹右键->New-> New Api(Proto) File ->Empty File/Api(Proto) Template,如图: 快速生成api/rpc服务 在目标文件夹右键->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code生成 方法一(工程区域) 对应文件(api、proto、sql)右键->New->Go Zero-> Api/Rpc/Model Code,如图: 方法二(编辑区域) 对应文件(api、proto、sql)右键-> Generate-> Api/Rpc/Model Code 错误提示 Live Template Live Template可以加快我们对api文件的编写,比如我们在go文件中输入main关键字根据tip回车后会插入一段模板代码 func main(){ } 或者说看到下图你会更加熟悉,曾几何时你还在这里定义过template 下面就进入今天api语法中的模板使用说明吧,我们先来看看service模板的效果 首先上一张图了解一下api文件中几个模板生效区域(psiTree元素区域) 预设模板及生效区域 模板关键字 psiTree生效区域 描述 @doc ApiService doc注释模板 doc ApiService doc注释模板 struct Struct struct声明模板 info ApiFile info block模板 type ApiFile type group模板 handler ApiService handler文件名模板 get ApiService get方法路由模板 head ApiService head方法路由模板 post ApiService post方法路由模板 put ApiService put方法路由模板 delete ApiService delete方法路由模板 connect ApiService connect方法路由模板 options ApiService options方法路由模板 trace ApiService trace方法路由模板 service ApiFile service服务block模板 json Tag、Tag literal tag模板 xml Tag、Tag literal tag模板 path Tag、Tag literal tag模板 form Tag、Tag literal tag模板 关于每个模板对应内容可在Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags中查看详细模板内容,如json tag模板内容为 json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"vscode.html":{"url":"vscode.html","title":"vscode插件","keywords":"","body":"vs code 插件 该插件可以安装在 1.46.0+ 版本的 Visual Studio Code 上,首先请确保你的 Visual Studio Code 版本符合要求,并已安装 goctl 命令行工具。如果尚未安装 Visual Studio Code,请安装并打开 Visual Studio Code。 导航到“扩展”窗格,搜索 goctl 并安装此扩展(发布者ID为 “xiaoxin-technology.goctl”)。 Visual Studio Code 扩展使用请参考这里。 功能列表 已实现功能 语法高亮 跳转到定义/引用 代码格式化 代码块提示 未实现功能: 语法错误检查 跨文件代码跳转 goctl 命令行调用 语法高亮 代码跳转 代码格式化 调用 goctl 命令行格式化工具,使用前请确认 goctl 已加入 $PATH 且有可执行权限 代码块提示 info 代码块 type 代码块 service 代码块 handler 代码块 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"分布式事务支持","keywords":"","body":"分布式事务支持 需求场景 在微服务架构中,当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多: 订单系统:需要保证创建订单和扣减库存要么同时成功,要么同时回滚 跨行转账场景:数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败 积分兑换场景:需要保证积分扣减和权益增加同时成功,或者同时失败 出行订票场景:需要在第三方系统同时定几张票,要么同时成功,要么全部取消 面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 解决方案 go-zero与dtm强强联合,推出了在go-zero中无缝接入dtm的极简方案,是go生态中首家提供分布式事务能力的微服务框架。该方案具备以下特征: dtm服务可以通过配置,直接注册到go-zero的注册中心 go-zero能够以内建的target格式访问dtm服务器 dtm能够识别go-zero的target格式,动态访问go-zero中的服务 详细的接入方式,参见dtm文档:go-zero支持 更多应用场景 dtm不仅可以解决上述的分布式事务场景,还可以解决更多的与数据一致性相关的场景,包括: 数据库与缓存一致性: dtm 的二阶段消息,能够保证数据库更新操作,和缓存更新/删除操作的原子性 秒杀系统: dtm 能够保证秒杀场景下,创建的订单量与库存扣减数量完全一样,无需后续的人工校准 多种存储组合: dtm 已支持数据库、Redis、Mongo等多种存储,可以将它们组合为一个全局事务,保证数据的一致性 更多 dtm 的能力和介绍,参见dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"plugin-center.html":{"url":"plugin-center.html","title":"插件中心","keywords":"","body":"插件中心 goctl api提供了对plugin命令来支持对api进行功能扩展,当goctl api中的功能不满足你的使用, 或者需要对goctl api进行功能自定义的扩展,那么插件功能将非常适合开发人员进行自给自足,详情见 goctl plugin 插件资源 goctl-go-compact goctl默认的一个路由一个文件合并成一个文件 goctl-swagger 通过api文件生成swagger文档 goctl-php goctl-php是一款基于goctl的插件,用于生成 php 调用端(服务端) http server请求代码 猜你想看 goctl插件 api语法介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"learning-resource.html":{"url":"learning-resource.html","title":"学习资源","keywords":"","body":"学习资源 这里将不定期更新go-zero的最新学习资源通道,目前包含通道有: 公众号 Go夜读 Go开源说 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"wechat.html":{"url":"wechat.html","title":"公众号","keywords":"","body":"公众号 微服务实践是go-zero的官方公众号,在这里会发布最新的go-zero最佳实践,同步go夜读、go开源说、GopherChina、腾讯云开发者大会等多渠道关于go-zero的最新技术和资讯。 公众号名称 公众号作者 公众号二维码 微服务实践 kevwan 推荐主题 《带你十天轻松搞定 Go 微服务》 该系列将带你在本机利用 go-zero 快速开发一个商城系统,向大家详细展示了基于 go-zero 框架构建微服务的过程,整个系列分十篇文章,目录结构如下: 第一天:环境搭建 第二天:服务拆分 第三天:用户服务 第四天:产品服务 第五天:订单服务 第六天:支付服务 第七天:RPC服务Auth验证 第八天:服务监控 第九天:链路追踪 第十天:分布式事务 干货 这里列举一些干货,想要收获更多go-zero最佳实践干货,可以关注公众号获取最新动态。 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"goreading.html":{"url":"goreading.html","title":"Go夜读","keywords":"","body":"Go夜读 2020-08-16 晓黑板 go-zero 微服务框架的架构设计 2020-10-03 go-zero 微服务框架和线上交流 防止缓存击穿之进程内共享调用 基于go-zero实现JWT认证 再见go-micro!企业项目迁移go-zero全攻略(一) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"gotalk.html":{"url":"gotalk.html","title":"Go开源说","keywords":"","body":"Go开源说 Go 开源说第四期 - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"redis-cache.html":{"url":"redis-cache.html","title":"go-zero缓存设计之持久层缓存","keywords":"","body":"go-zero缓存设计之持久层缓存 缓存设计原理 我们对缓存是只删除,不做更新,一旦DB里数据出现修改,我们就会直接删除对应的缓存,而不是去更新。 我们看看删除缓存的顺序怎样才是正确的。 先删除缓存,再更新DB 我们看两个并发请求的情况,A请求需要更新数据,先删除了缓存,然后B请求来读取数据,此时缓存没有数据,就会从DB加载数据并写回缓存,然后A更新了DB,那么此时缓存内的数据就会一直是脏数据,直到缓存过期或者有新的更新数据的请求。如图 先更新DB,再删除缓存 A请求先更新DB,然后B请求来读取数据,此时返回的是老数据,此时可以认为是A请求还没更新完,最终一致性,可以接受,然后A删除了缓存,后续请求都会拿到最新数据,如图 让我们再来看一下正常的请求流程: 第一个请求更新DB,并删除了缓存 第二个请求读取缓存,没有数据,就从DB读取数据,并回写到缓存里 后续读请求都可以直接从缓存读取 我们再看一下DB查询有哪些情况,假设行记录里有ABCDEFG七列数据: 只查询部分列数据的请求,比如请求其中的ABC,CDE或者EFG等,如图 查询单条完整行记录,如图 查询多条行记录的部分或全部列,如图 对于上面三种情况,首先,我们不用部分查询,因为部分查询没法缓存,一旦缓存了,数据有更新,没法定位到有哪些数据需要删除;其次,对于多行的查询,根据实际场景和需要,我们会在业务层建立对应的从查询条件到主键的映射;而对于单行完整记录的查询,go-zero 内置了完整的缓存管理方式。所以核心原则是:go-zero 缓存的一定是完整的行记录。 下面我们来详细介绍 go-zero 内置的三种场景的缓存处理方式: 基于主键的缓存PRIMARY KEY (`id`) 这种相对来讲是最容易处理的缓存,只需要在 redis 里用 primary key 作为 key 来缓存行记录即可。 基于唯一索引的缓存 在做基于索引的缓存设计的时候我借鉴了 database 索引的设计方法,在 database 设计里,如果通过索引去查数据时,引擎会先在 索引->主键 的 tree 里面查找到主键,然后再通过主键去查询行记录,就是引入了一个间接层去解决索引到行记录的对应问题。在 go-zero 的缓存设计里也是同样的原理。 基于索引的缓存又分为单列唯一索引和多列唯一索引: 但是对于 go-zero 来说,单列和多列只是生成缓存 key 的方式不同而已,背后的控制逻辑是一样的。然后 go-zero 内置的缓存管理就比较好的控制了数据一致性问题,同时也内置防止了缓存的击穿、穿透、雪崩问题(这些在 gopherchina 大会上分享的时候仔细讲过,见后续 gopherchina 分享视频)。 另外,go-zero 内置了缓存访问量、访问命中率统计,如下所示: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 可以看到比较详细的统计信息,便于我们来分析缓存的使用情况,对于缓存命中率极低或者请求量极小的情况,我们就可以去掉缓存了,这样也可以降低成本。 单列唯一索引如下: UNIQUE KEY `product_idx` (`product`) 多列唯一索引如下: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) 缓存代码解读 1.基于主键的缓存逻辑 具体实现代码如下: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } 这里的 Take 方法是先从缓存里去通过 key 拿数据,如果拿到就直接返回,如果拿不到,那么就通过 query 方法去 DB 读取完整行记录并写回缓存,然后再返回数据。整个逻辑还是比较简单易懂的。 我们详细看看 Take 的实现: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } Take 的逻辑如下: 用 key 从缓存里查找数据 如果找到,则返回数据 如果找不到,用 query 方法去读取数据 读到后调用 c.SetCache(key, v) 设置缓存 其中的 doTake 代码和解释如下: // v - 需要读取的数据对象 // key - 缓存key // query - 用来从DB读取完整数据的方法 // cacheVal - 用来写缓存的方法 func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // 用barrier来防止缓存击穿,确保一个进程内只有一个请求去加载key对应的数据 val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // 从cache里读取数据 if err := c.doGetCache(key, v); err != nil { // 如果是预先放进来的placeholder(用来防止缓存穿透)的,那么就返回预设的errNotFound // 如果是未知错误,那么就直接返回,因为我们不能放弃缓存出错而直接把所有请求去请求DB, // 这样在高并发的场景下会把DB打挂掉的 if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // 请求DB // 如果返回的error是errNotFound,那么我们就需要在缓存里设置placeholder,防止缓存穿透 if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // 统计DB失败 c.stat.IncrementDbFails() return nil, err } // 把数据写入缓存 if err = cacheVal(v); err != nil { logx.Error(err) } } // 返回json序列化的数据 return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // 把数据写入到传入的v对象里 return jsonx.Unmarshal(val.([]byte), v) } 2. 基于唯一索引的缓存逻辑 因为这块比较复杂,所以我用不同颜色标识出来了响应的代码块和逻辑,block 2 其实跟基于主键的缓存是一样的,这里主要讲 block 1 的逻辑。 代码块的 block 1 部分分为两种情况: 通过索引能够从缓存里找到主键,此时就直接用主键走 block 2 的逻辑了,后续同上面基于主键的缓存逻辑 通过索引无法从缓存里找到主键 通过索引从DB里查询完整行记录,如有 error,返回 查到完整行记录后,会把主键到完整行记录的缓存和索引到主键的缓存同时写到 redis 里 返回所需的行记录数据 // v - 需要读取的数据对象 // key - 通过索引生成的缓存key // keyer - 用主键生成基于主键缓存的key的方法 // indexQuery - 用索引从DB读取完整数据的方法,需要返回主键 // primaryQuery - 用主键从DB获取完整数据的方法 func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // 先通过索引查询缓存,看是否有索引到主键的缓存 if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // 如果没有索引到主键的缓存,那么就通过索引查询完整数据 primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // 通过索引查询到了完整数据,设置found,后面直接使用,不需要再从缓存读取数据了 found = true // 将主键到完整数据的映射保存到缓存里,TakeWithExpire方法已经将索引到主键的映射保存到缓存了 return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // 已经通过索引找到了数据,直接返回即可 if found { return nil } // 通过主键从缓存读取数据,如果缓存没有,通过primaryQuery方法从DB读取并回写缓存再返回数据 return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } 我们来看一个实际的例子 func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // 生成基于索引的key indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // 基于主键生成完整数据缓存的key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // 基于索引的DB查询方法 func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // 基于主键的DB查询方法 func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // 错误处理,需要判断是否返回的是sqlc.ErrNotFound,如果是,我们用本package定义的ErrNotFound返回 // 避免使用者感知到有没有使用缓存,同时也是对底层依赖的隔离 switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } 所有上面这些缓存的自动管理代码都是可以通过 goctl 自动生成的,我们团队内部 CRUD 和缓存基本都是通过 goctl 自动生成的,可以节省大量开发时间,并且缓存代码本身也是非常容易出错的,即使有很好的代码经验,也很难每次完全写对,所以我们推荐尽可能使用自动的缓存代码生成工具去避免错误。 猜你想看 Go开源说第四期-go-zero缓存如何设计 Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"go-zero缓存设计之业务层缓存","keywords":"","body":"go-zero缓存设计之业务层缓存 在上一篇go-zero缓存设计之持久层缓存介绍了db层缓存,回顾一下,db层缓存主要设计可以总结为: 缓存只删除不更新 行记录始终只存储一份,即主键对应行记录 唯一索引仅缓存主键值,不直接缓存行记录(参考mysql索引思想) 防缓存穿透设计,默认一分钟 不缓存多行记录 前言 在大型业务系统中,通过对持久层添加缓存,对于大多数单行记录查询,相信缓存能够帮持久层减轻很大的访问压力,但在实际业务中,数据读取不仅仅只是单行记录, 面对大量多行记录的查询,这对持久层也会造成不小的访问压力,除此之外,像秒杀系统、选课系统这种高并发的场景,单纯靠持久层的缓存是不现实的,本节我们来 介绍go-zero实践中的缓存设计——biz缓存。 适用场景举例 选课系统 内容社交系统 秒杀 ... 像这些系统,我们可以在业务层再增加一层缓存来存储系统中的关键信息,如选课系统中学生选课信息,课程剩余名额;内容社交系统中某一段时间之间的内容信息等。 接下来,我们一内容社交系统来进行举例说明。 在内容社交系统中,我们一般是先查询一批内容列表,然后点击某条内容查看详情, 在没有添加biz缓存前,内容信息的查询流程图应该为: 从图以及上一篇文章go-zero缓存设计之持久层缓存中我们可以知道,内容列表的获取是没办法依赖缓存的, 如果我们在业务层添加一层缓存用来存储列表中的关键信息(甚至完整信息),那么多行记录的访问不在是一个问题,这就是biz redis要做的事情。 接下来我们来看一下设计方案,假设内容系统中单行记录包含以下字段 字段名称 字段类型 备注 id string 内容id title string 标题 content string 详细内容 createTime time.Time 创建时间 我们的目标是获取一批内容列表,而尽量避免内容列表走db造成访问压力,首先我们采用redis的sort set数据结构来存储,根需要存储的字段信息量,有两种redis存储方案: 缓存局部信息 对其关键字段信息(如:id等)按照一定规则压缩,并存储,score我们用createTime毫秒值(时间值相等这里不讨论),这种存储方案的好处是节约redis存储空间, 那另一方面,缺点就是需要对列表详细内容进行二次回查(但这次回查是会利用到持久层的行记录缓存的) 缓存完整信息 对发布的所有内容按照一定规则压缩后均进行存储,同样score我们还是用createTime毫秒值,这种存储方案的好处是业务的增、删、查、改均走redis,而db层这时候 就可以不用考虑行记录缓存了,持久层仅提供数据备份和恢复使用,从另一方面来看,其缺点也很明显,需要的存储空间、配置要求更高,费用也会随之增大。 示例代码: type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent 提供内容存储 func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent 提供内容删除 func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // 内容压缩 func compress(c *Content) string { // todo: do it yourself var ret string return ret } // 内容解压 func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime提供根据时间段进行数据查询 func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } 在以上例子中,redis是没有设置过期时间的,我们将增、删、改、查操作均同步到redis,我们认为内容社交系统的列表访问请求是比较高的情况下才做这样的方案设计, 除此之外,还有一些数据访问,没有想内容设计系统这么频繁的访问, 可能是某一时间段内访问量突如其来的增加,之后可能很长一段时间才会再访问一次,以此间隔, 或者说不会再访问了,面对这种场景,如果我又该如何考虑缓存的设计呢?在go-zero内容实践中,有两种方案可以解决这种问题: 增加内存缓存:通过内存缓存来存储当前可能突发访问量比较大的数据,常用的存储方案采用map数据结构来存储,map数据存储实现比较简单,但缓存过期处理则需要增加 定时器来出来,另一宗方案是通过go-zero库中的 Cache ,其是专门 用于内存管理. 采用biz redis,并设置合理的过期时间 总结 以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务 监控来衡量时候需要引入biz。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-queue.html":{"url":"go-queue.html","title":"go-zero分布式定时任务","keywords":"","body":" go-zero 分布式定时任务 日常任务开发中,我们会有很多异步、批量、定时、延迟任务要处理,go-zero中有go-queue,推荐使用go-queue去处理,go-queue本身也是基于go-zero开发的,其本身是有两种模式 dq : 依赖于beanstalkd,分布式,可存储,延迟、定时设置,关机重启可以重新执行,消息不会丢失,使用非常简单,go-queue中使用了redis setnx保证了每条消息只被消费一次,使用场景主要是用来做日常任务使用 kq:依赖于kafka,这个就不多介绍啦,大名鼎鼎的kafka,使用场景主要是做消息队列 我们主要说一下dq,kq使用也一样的,只是依赖底层不同,如果没使用过beanstalkd,没接触过beanstalkd的可以先google一下,使用起来还是挺容易的。 etc/job.yaml : 配置文件 Name: job Log: ServiceName: job Level: info #dq依赖Beanstalks、redis ,Beanstalks配置、redis配置 DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go :解析dq对应etc/*.yaml配置 /** * @Description 配置文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : 负责注册多任务 /** * @Description 注册job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) } ProducerLogic: 其中一个job业务逻辑 /** * @Description 生产者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i 另外一个Job业务逻辑 /** * @Description 消费者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description 配置 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go启动文件 /** * @Description 启动文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() //配置 var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) //注册job group := service.NewServiceGroup() handler.RegisterJob(ctx,group) //捕捉信号 ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { for { s := 常见问题: 为什么使用dq,需要使用redis? 因为beanstalk是单点服务,无法保证高可用。dq可以使用多个单点beanstalk服务,互相备份 & 保证高可用。使用redis解决重复消费问题。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"datacenter.html":{"url":"datacenter.html","title":"我是如何用go-zero 实现一个中台系统","keywords":"","body":"我是如何用 go-zero 实现一个中台系统 作者:Jack Luo 原文连接:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] 最近发现golang社区里出了一个新星的微服务框架,来自好未来,光看这个名字,就很有奔头,之前,也只是玩过go-micro,其实真正的还没有在项目中运用过,只是觉得 微服务,grpc 这些很高大尚,还没有在项目中,真正的玩过,我看了一下官方提供的工具真的很好用,只需要定义好,舒适文件jia结构 都生成了,只需要关心业务,加上最近 有个投票的活动,加上最近这几年中台也比较火,所以决定玩一下, 开源地址: https://github.com/jackluo2012/datacenter 先聊聊中台架构思路吧: 中台的概念大概就是把一个一个的app 统一起来,反正我是这样理解的。 先聊用户服务吧,现在一个公司有很多的公众号、小程序、微信的、支付宝的,还有 xxx xxx,很多的平台,每次开发的时候,我们总是需要做用户登陆的服务,不停的复制代码,然后我们就在思考能不能有一套独立的用户服务,只需要告诉我你需要传个你要登陆的平台(比如微信),微信登陆,需要的是客户端返回给服务端一个code ,然后服务端拿着这个code去微信获取用户信息,反正大家都明白。 我们决定,将所有的信息弄到配置公共服务中去,里面再存微信、支付宝以及其它平台的appid、appkey、还有支付的appid、appkey,这样就写一套。 最后说说实现吧,整个就一个repo: 网关,我们用的是: go-zero的Api服务 其它它的是服务,我们就是用的go-zero的rpc服务 看下目录结构 整个项目完成,我一个人操刀,写了1个来星期,我就实现了上面的中台系统。 datacenter-api服务 先看官方文档 https://go-zero.dev/cn/ 我们先把网关搭建起来: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter 查看book目录: ➜ datacenter tree . └── go.mod 0 directories, 1 file 创建api文件 ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api ├── user.api #用户 ├── votes.api #投票 ├── search.api #搜索 ├── questions.api #问答 └── go.mod 定义api服务 分别包含了上面的 公共服务,用户服务,投票活动服务 datacenter.api的内容: info( title: \"中台系统\"// TODO: add title desc: \"中台系统\"// TODO: add description author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) import \"user.api\" import \"votes.api\" import \"search.api\" import \"questions.api\" //获取 应用信息 type Beid { Beid int64 `json:\"beid\"` } type Token { Token string `json:\"token\"` } type WxTicket { Ticket string `json:\"ticket\"` } type Application { Sname string `json:\"Sname\"` //名称 Logo string `json:\"logo\"` // login Isclose int64 `json:\"isclose\"` //是否关闭 Fullwebsite string `json:\"fullwebsite\"` // 全站名称 } type SnsReq { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 BackUrl string `json:\"back_url\"` //登陆返回的地址 } type SnsResp { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 Appid string `json:\"appid\"` //sns 平台的id Title string `json:\"title\"` //名称 LoginUrl string `json:\"login_url\"` //微信登陆的地址 } type WxShareResp { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"获取站点的信息\" ) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"获取站点的社交属性信息\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) //获取分享的 @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } //上传需要登陆 @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"七牛上传凭证\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } user.api内容 //注册请求 type RegisterReq struct { // TODO: add members here and delete this comment Mobile string `json:\"mobile\"` //基本一个手机号码就完事 Password string `json:\"password\"` Smscode string `json:\"smscode\"` //短信码 } //登陆请求 type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` //1.密码登陆,2.短信登陆 Password string `json:\"password\"` } //微信登陆 type WxLoginReq struct { Beid int64 `json:\"beid\"` //应用id Code string `json:\"code\"` //微信登陆密钥 Ptyid int64 `json:\"ptyid\"` //对应平台 } //返回用户信息 type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } //返回APPUser type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service datacenter-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service datacenter-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } votes.api 投票内容 // 投票活动api type Actid struct { Actid int64 `json:\"actid\"` //活动id } type VoteReq struct { Aeid int64 `json:\"aeid\"` // 作品id Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` //投票票数 Viewcount int64 `json:\"viewcount\"` //浏览数 } // 活动返回的参数 type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` //活动名称 Descr string `json:\"descr\"` //活动描述 StartDate int64 `json:\"start_date\"` //活动时间 EnrollDate int64 `json:\"enroll_date\"` //投票时间 EndDate int64 `json:\"end_date\"` //活动结束时间 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 Type int64 `json:\"type\"` //投票方式 Num int64 `json:\"num\"` //投票几票 } //报名 type EnrollReq struct { Actid Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 } // 作品返回 type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` // 作品id Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 } @server( group: votes ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"活动访问+1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"获取报名的投票作品信息\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"获取报名的投票作品列表\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service datacenter-api { @doc( summary: \"投票\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } questions.api 问答内容: // 问答 抽奖 开始 @server( group: questions ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activitiesInfo get /questions/activities/info (Actid) returns (ActivityResp) @doc( summary: \"获取奖品信息\" ) @handler awardInfo get /questions/award/info (Actid) returns (ActivityResp) @handler awardList get /questions/award/list (Actid) returns (ActivityResp) } type AnswerReq struct { ActivityId int64 `json:\"actid\"` Answers string `json:\"answers\"` Score string `json:\"score\"` } type QuestionsAwardReq struct { ActivityId int64 `json:\"actid\"` AnswerId int64 `json:\"answerid\"` } type AnswerResp struct { Answers string `json:\"answers\"` Score string `json:\"score\"` } type AwardConvertReq struct { UserName string `json:\"username\"` Phone string `json:\"phone\"` LotteryId int64 `json:\"lotteryid\"` } @server( jwt: Auth group: questions middleware: Usercheck ) service datacenter-api { @doc( summary: \"获取题目\" ) @handler lists get /questions/lists (VoteReq) returns (AnswerResp) @doc( summary: \"提交答案\" ) @handler change post /questions/change (AnswerReq) returns (VoteResp) @doc( summary: \"获取分数\" ) @handler grade get /questions/grade (VoteReq) returns (VoteResp) @doc( summary: \"开始转盘\" ) @handler turntable post /questions/lottery/turntable (EnrollReq) returns (EnrollResp) @doc( summary: \"填写中奖信息人\" ) @handler lottery post /questions/lottery/convert (AwardConvertReq) returns (EnrollResp) } // 问答 抽奖 结束 search.api 搜索 type SearchReq struct { Keyword string `json:\"keyword\"` Page string `json:\"page\"` Size string `json:\"size\"` } type SearchResp struct { Data []ArticleReq `json:\"data\"` } type ArticleReq struct{ NewsId string `json:\"NewsId\"` NewsTitle string `json:\"NewsTitle\"` ImageUrl string `json:\"ImageUrl\"` } @server( group: search middleware: Admincheck ) service datacenter-api { @doc( summary: \"搜索\" ) @handler article get /search/article (SearchReq) returns (SearchResp) @handler articleInit get /search/articel/init (SearchReq) returns (SearchResp) @handler articleStore post /search/articel/store (ArticleReq) returns (ArticleReq) } 上面基本上写就写的API及文档的思路 生成datacenter api服务 ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter treer . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files 我们打开 etc/datacenter-api.yaml 把必要的配置信息加上 Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: 你的jwtwon Secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: 密码 Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc 上面的 UserRpc, CommonRpc ,还有 VotesRpc 这些我先写上,后面再来慢慢加。 我们先来写 CommonRpc 服务。 CommonRpc服务 新建项目目录 ➜ datacenter mkdir -p common/rpc && cd common/rpc 直接就新建在了,datacenter目录中,因为common 里面,可能以后会不只会提供rpc服务,可能还有api的服务,所以又加了rpc目录 goctl创建模板 ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto 往里面填入内容: ➜ rpc cat common.proto syntax = \"proto3\"; option go_package = \"common\"; package common; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } // 请求的api message AppConfigReq { int64 beid=1; int64 ptyid=2; } // 返回的值 message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl生成rpc服务 ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files 基本上,就把所有的目录规范和结构的东西都生成了,就不用纠结项目目录了,怎么放了,怎么组织了。 看一下,配置信息,里面可以写入mysql和其它redis的信息: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc 我们再来加上数据库服务: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files 这样基本的一个 rpc 就写完了,然后我们将rpc 和model 还有api串连起来,这个官方的文档已经很详细了,这里就只是贴一下代码: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } 再在svc中修改: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } 上面的代码已经将 rpc 和 model 数据库关联起来了,我们现在再将 rpc 和 api 关联起来: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } 加入 svc 服务中: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"调用 %s 方法 耗时: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } 这样基本上,我们就可以在 logic 的文件目录中调用了: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { //检查 缓存中是否有值 err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } 这样,基本就连接起来了,其它基本上就不用改了,UserRPC, VotesRPC 类似,这里就不在写了。 使用心得 go-zero 的确香,因为它有一个 goctl 的工具,他可以自动的把代码结构全部的生成好,我们就不再去纠结,目录结构 ,怎么组织,没有个好几年的架构能力是不好实现的,有什么规范那些,并发,熔断,完全不用,考虑其它的,专心的实现业务就好,像微服务,还要有服务发现,一系列的东西,都不用关心,因为 go-zero 内部已经实现了。 我写代码也写了有10多年了,之前一直用的 php,比较出名的就 laravel,thinkphp,基本上就是模块化的,像微服务那些实现真的有成本,但是你用上go-zero,你就像调api接口一样简单的开发,其它什么服务发现,那些根本就不用关注了,只需要关注业务。 一个好的语言,框架,他们的底层思维,永远都是效率高,不加班的思想,我相信go-zero会提高你和你团队或是公司的效率。go-zero的作者说,他们有个团队专门整理go-zero框架,目的也应该很明显,那就是提高,他们自己的开发效率,流程化,标准化,是提高工作效率的准则,像我们平时遇到了问题,或是遇到了bug,我第一个想到的不是怎么去解决我的bug,而是在想我的流程是不是有问题,我的哪个流程会导致bug,最后我相信 go-zero 能成为 微服务开发 的首选框架。 最后说说遇到的坑吧: grpc grpc 本人第一次用,然后就遇到了,有些字符为空时,字段值不显示的问题: 通过 grpc 官方库中的 jsonpb 来实现,官方在它的设定中有一个结构体用来实现 protoc buffer 转换为JSON结构,并可以根据字段来配置转换的要求。 跨域问题 go-zero 中设置了,感觉没有效果,大佬说通过nginx 设置,后面发现还是不行,最近强行弄到了一个域名下,后面有时间再解决。 sqlx go-zero 的 sqlx 问题,这个真的费了很长的时间: time.Time 这个数据结构,数据库中用的是 timestamp 这个 比如我的字段 是delete_at 默认数库设置的是null ,结果插入的时候,就报了 Incorrect datetime value: '0000-00-00' for column 'deleted_at' at row 1\"} 这个错,查询的时候报 deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" 后面果断去掉了这个字段,字段上面加上 .omitempty 这个标签,好像也有用,db:\".omitempty\" 其次就是这个 Conversion from collation utf8_general_ci into utf8mb4_unicode_ci,这个导致的大概原因是,现在都喜欢用emj表情了,mysql数据识别不了。 数据连接 mysql 这边照样按照原始的方式,将配置文件修改编码格式,重新创建数据库,并且设置数据库编码为utf8mb4,排序规则为 utf8mb4_unicode_ci。 这样的话,所有的表还有string字段都是这个编码格式,如果不想所有的都是,可以单独设置,这个不是重点.因为在navicat上都好设置,手动点一下就行了。 重点来了:golang中使用的是 github.com/go-sql-driver/mysql 驱动,将连接 mysql的 dsn(因为我这使用的是gorm,所以dsn可能跟原生的格式不太一样,不过没关系, 只需要关注 charset 和 collation 就行了) root:password@/name?parseTime=True&loc=Local&charset=utf8 修改为: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"stream.html":{"url":"stream.html","title":"流数据处理利器","keywords":"","body":"流数据处理利器 流处理 (Stream processing) 是一种计算机编程范式,其允许给定一个数据序列 (流处理数据源),一系列数据操作 (函数) 被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到 Kafka,然后再由 Job 消费 Kafaka 写到 elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx go-zero 是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx ,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce 工具进行处理。 总结 本篇文章介绍了流处理的基本概念和go-zero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"online-exchange.html":{"url":"online-exchange.html","title":"10月3日线上交流问题汇总","keywords":"","body":"10月3日线上交流问题汇总 go-zero适用场景 希望说说应用场景,各个场景下的优势 高并发的微服务系统 支撑千万级日活,百万级QPS 完整的微服务治理能力 支持自定义中间件 很好的管理了数据库和缓存 有效隔离故障 低并发的单体系统 这种系统直接使用api层即可,无需rpc服务 各个功能的使用场景以及使用案例 限流 熔断 降载 超时 可观测性 go-zero的实际体验 服务很稳 前后端接口一致性,一个api文件即可生成前后端代码 规范、代码量少,意味着bug少 免除api文档,极大降低沟通成本 代码结构完全一致,便于维护和接手 微服务的项目结构, monorepo的 CICD 处理 bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model mono repo的CI我们是通过gitlab做的,CD使用jenkins CI尽可能更严格的模式,比如-race,使用sonar等工具 CD有开发、测试、预发、灰度和正式集群 晚6点上灰度、无故障的话第二天10点自动同步到正式集群 正式集群分为多个k8s集群,有效的防止单集群故障,直接摘除即可,集群升级更有好 如何部署,如何监控? 全量K8S,通过jenkins自动打包成docker镜像,按照时间打包tag,这样可以一眼看出哪一天的镜像 上面已经讲了,预发->灰度->正式 Prometheus+自建dashboard服务 基于日志检测服务和请求异常 如果打算换go-zero框架重构业务,如何做好线上业务稳定安全用户无感切换?另外咨询下如何进行服务划分? 逐步替换,从外到内,加个proxy来校对,校对一周后可以切换 如有数据库重构,则需要做好新老同步 服务划分按照业务来,遵循从粗到细的原则,避免一个api一个微服务 数据拆分对于微服务来讲尤为重要,上层好拆,数据难拆,尽可能保证按照业务拆分数据 服务发现 服务发现 etcd 的 key 的设计 服务key+时间戳,服务进程数存在时间戳冲突的概率极低,忽略 etcd服务发现与治理, 异常捕获与处理异常 为啥k8s还使用etcd做服务发现,因为dns的刷新有延迟,导致滚动更新会有大量失败,而etcd可以做到完全无损更新 etcd集群直接部署在k8s集群内,因为多个正式集群,集群单点和注册避免混乱 针对etcd异常或者leader切换,自动侦测并刷新,当etcd有异常不能恢复时,不会刷新服务列表,保障服务依然可用 缓存的设计与使用案例 分布式多redis集群,线上最大几十个集群为同一个服务提供缓存服务 无缝扩缩容 不存在没有过期时间的缓存,避免大量不常使用的数据占用资源,默认一周 缓存穿透,没有的数据会短暂缓存一分钟,避免刷接口或大量不存在的数据请求带垮系统 缓存击穿,一个进程只会刷新一次同一个数据,避免热点数据被大量同时加载 缓存雪崩,对缓存过期时间自动做了jitter,5%的标准变差,使得一周的过期时间分布在16小时内,有效防止了雪崩 我们线上数据库都有缓存,否则无法支撑海量并发 自动缓存管理已经内置于go-zero,并可以通过goctl自动生成代码 能否讲解下, 中间件,拦截器的设计思想 洋葱模型 本中间件处理,比如限流,熔断等,然后决定是否调用next next调用 对next调用返回结果做处理 微服务的事务处理怎么实现好,gozero分布式事务设计和实现,有什么好中间件推荐 2PC,两阶段提交 TCC,Try-Confirm-Cancel 消息队列,最大尝试 人工补偿 多级 goroutine 的异常捕获 ,怎么设计比较好 微服务系统请求异常应该隔离,不能让单个异常请求带崩整个进程 go-zero自带了RunSafe/GoSafe,用来防止单个异常请求导致进程崩溃 监控需要跟上,防止异常过量而不自知 fail fast和故障隔离的矛盾点 k8s配置的生成与使用(gateway, service, slb) 内部自动生成k8s的yaml文件,过于依赖配置而未开源 打算在bookstore的示例里加上k8s配置样板 slb->nginx->nodeport->api gateway->rpc service gateway限流、熔断和降载 限流分为两种:并发控制和分布式限流 并发控制用来防止瞬间过量请求,保护系统不被打垮 分布式限流用来给不同服务配置不同的quota 熔断是为了对依赖的服务进行保护,当一个服务出现大量异常的时候,调用者应该给予保护,使其有机会恢复正常,同时也达到fail fast的效果 降载是为了保护当前进程资源耗尽而陷入彻底不可用,确保尽可能服务好能承载的最大请求量 降载配合k8s,可以有效保护k8s扩容,k8s扩容分钟级,go-zero降载秒级 介绍core中好用的组件,如timingwheel等,讲讲设计思路 布隆过滤器 进程内cache RollingWindow TimingWheel 各种executors fx包,map/reduce/filter/sort/group/distinct/head/tail... 一致性hash实现 分布式限流实现 mapreduce,带cancel能力 syncx包里有大量的并发工具 如何快速增加一种rpc协议支持,將跨机发现改为调本机节点,并关闭复杂filter和负载均衡功能 go-zero跟grpc关系还是比较紧密的,设计之初没有考虑支持grpc以外的协议 如果要增加的话,那就只能fork出来魔改了 调本机直接用direct的scheme即可 为啥要去掉filter和负载均衡?如果要去的话,fork了改,但没必要 日志和监控和链路追踪的设计和实现思路,最好有大概图解 日志和监控我们使用prometheus, 自定义dashboard服务,捆绑提交数据(每分钟) 链路追踪可以看出调用关系,自动记录trace日志 go-zero框架有用到什么池化技术吗?如果有,在哪些core代码里面可以参考 一般不需要提前优化,过度优化是大忌 core/syncx/pool.go里面定义了带过期时间的通用池化技术 go-zero用到了那些性能测试方法框架,有代码参考吗?可以说说思路和经验 go benchmark 压测可以通过现有业务日志样本,来按照预估等比放大 压测一定要压到系统扛不住,看第一个瓶颈在哪里,改完再压,循环 说一下代码的抽象经验和心得 Don’t repeat yourself 你未必需要它,之前经常有业务开发人员问我可不可以增加这个功能或那个功能,我一般都会仔细询问深层次目的,很多时候会发现其实这个功能是多余的,不需要才是最佳实践 Martin Fowler提出出现三次再抽象的原则,有时有些同事会找我往框架里增加一个功能,我思考后经常会回答这个你先在业务层写,其它地方也有需要了你再告诉我,三次出现我会考虑集成到框架里 一个文件应该尽量只做一件事,每个文件尽可能控制在200行以内,一个函数尽可能控制在50行以内,这样不需要滚动就可以看到整个函数 需要抽象和提炼的能力,多去思考,经常回头思考之前的架构或实现 你会就go-zero 框架从设计到实践出书吗?框架以后的发展规划是什么? 暂无出书计划,做好框架是最重要的 继续着眼于工程效率 提升服务治理能力 帮助业务开发尽可能快速落地 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"contributor.html":{"url":"contributor.html","title":"贡献人员","keywords":"","body":"社区贡献 作者 kevwan go-zero参与人员 kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy heyanfu sjatsh Mikaelemmmm foliet taobig Suyghur wubenqi shenbaise9527 fondoger reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl weicut zzzfwww HarryWang29 soasurs workman-Lu wuqinqiang ShyunnY appleboy Howie59 pipi-lv AlexLast almas1992 SnakeHacker sohamtembhurne toby1991 kscooo bensonfx cuishuang fyyang wangzeping722 guonaihong heyehang masonchen2014 lchjczw lucaq ahmczsy moyrne mongobaba mlboy me-cs yangwenmai magickeha lowang-bh lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang zeromake 2822132073 linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 lovelly zzhaolei zzZZzzz888 sixwaaaay liuqing6767 lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle peasfarmer qwxingzhe SeigeC jsonMark RivenChan toventang vankillua shssen rcyw weibobo windk wojiukankan wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 yiGmMk liyiwu l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen cuisongliu dahaihu dylanNew edieruby elza2 fffreedom codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog 文档贡献人员 kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:18 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"文档贡献","keywords":"","body":"文档贡献 怎么贡献文档? 点击顶部\"编辑此页\"按钮即可进入源码仓库对应的文件,开发人员将修改(添加)的文档通过pr形式提交, 我们收到pr后会进行文档审核,一旦审核通过即可更新文档。 可以贡献哪些文档? 文档编写错误 文档不规范、不完整 go-zero应用实践、心得 组件中心 文档pr通过后文档多久会更新? 在pr接受后,github action会自动build gitbook并发布,因此在github action成功后1-2分钟即可查看更新后的文档。 文档贡献注意事项 纠错、完善源文档可以直接编写原来的md文件 新增组件文档需要保证文档排版、易读,且组件文档需要放在组件中心子目录中 go-zero应用实践分享可以直接放在开发实践子目录下 目录结构规范 目录结构不宜过深,最好不要超过3层 组件文档需要在归属到组件中心,如* [开发实践](practise.md) * [logx](logx.md) * [bloom](bloom.md) * [executors](executors.md) * 你的文档目录名称 应用实践需要归属到开发实践,如* [开发实践](practise.md) * [我是如何用go-zero 实现一个中台系统](datacenter.md) * [流数据处理利器](stream.md) * [10月3日线上交流问题汇总](online-exchange.md * 你的文档目录名称 开发实践文档模板 # 标题 > 作者:填入作者名称 > > 原文连接: 原文连接 some markdown content 猜你想看 怎么参与贡献 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"error.html":{"url":"error.html","title":"常见错误处理","keywords":"","body":"常见错误处理 Windows上报错 A required privilege is not held by the client. 解决方法:\"以管理员身份运行\" goctl 即可。 grpc引起错误 错误一 protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. 解决方法: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go安装失败 go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout 请确认GOPROXY已经设置,GOPROXY设置见go module配置 api服务启动失败 error: config file etc/user-api.yaml, error: type mismatch for field xx 请确认user-api.yaml配置文件中配置项是否已经配置,如果有值,检查一下yaml配置文件是否符合yaml格式。 goctl找不到 command not found: goctl 请确保goctl已经安装或者goctl是否已经添加到环境变量 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"source.html":{"url":"source.html","title":"相关源码","keywords":"","body":"相关源码 demo源码 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"tips.html":{"url":"tips.html","title":"阅读须知","keywords":"","body":"阅读须知 本文档从快速入门,详细项目开发流程,go-zero服务设计思想,goctl工具的使用等维度进行了介绍, 对于刚刚接触go或go-zero的同学需要把这些篇幅都看完才能有所了解,因此有些费力,这里建议大家阅读的方法。 保持耐心跟着文档目录进行,文档是按照从简单到深入的渐进式过程编写的。 在遇到问题或错误时,请一定记住多查FAQ。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"shorturl.html":{"url":"shorturl.html","title":"快速构建高并发微服务","keywords":"","body":"快速构建高并发微服务 English | 简体中文 0. 为什么说做好微服务很难 要想做好微服务,我们需要理解和掌握的知识点非常多,从几个维度上来说: 基本功能层面 并发控制 & 限流,避免服务被突发流量击垮 服务注册与服务发现,确保能够动态侦测增减的节点 负载均衡,需要根据节点承受能力分发流量 超时控制,避免对已超时请求做无用功 熔断设计,快速失败,保障故障节点的恢复能力 高阶功能层面 请求认证,确保每个用户只能访问自己的数据 链路追踪,用于理解整个系统和快速定位特定请求的问题 日志,用于数据收集和问题定位 可观测性,没有度量就没有优化 对于其中每一点,我们都需要用很长的篇幅来讲述其原理和实现,那么对我们后端开发者来说,要想把这些知识点都掌握并落实到业务系统里,难度是非常大的,不过我们可以依赖已经被大流量验证过的框架体系。go-zero 微服务框架就是为此而生。 另外,我们始终秉承 工具大于约定和文档 的理念。我们希望尽可能减少开发人员的心智负担,把精力都投入到产生业务价值的代码上,减少重复代码的编写,所以我们开发了 goctl 工具。 下面我通过短链微服务来演示通过 go-zero 快速的创建微服务的流程,走完一遍,你就会发现:原来编写微服务如此简单! 1. 什么是短链服务 短链服务就是将长的 URL 网址,通过程序计算等方式,转换为简短的网址字符串。 写此短链服务是为了从整体上演示 go-zero 构建完整微服务的过程,算法和实现细节尽可能简化了,所以这不是一个高阶的短链服务。 2. 短链微服务架构图 这里只用了 Transform RPC 一个微服务,并不是说 API Gateway 只能调用一个微服务,只是为了最简演示 API Gateway 如何调用 RPC 微服务而已 在真正项目里要尽可能每个微服务使用自己的数据库,数据边界要清晰 3. goctl 各层代码生成一览 所有绿色背景的功能模块是自动生成的,按需激活,红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑,各层示意图分别如下: API Gateway RPC model 下面我们来一起完整走一遍快速构建微服务的流程,Let’s Go!🏃‍♂️ 4. 准备工作 安装 etcd, mysql, redis 安装 protoc-gen-go $ go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 安装 protoc $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip $ unzip protoc-3.14.0-linux-x86_64.zip $ mv bin/protoc /usr/local/bin/ 安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 创建工作目录 shorturl 和 shorturl/api mkdir -p shorturl/api 在 shorturl 目录下执行 go mod init shorturl 初始化 go.mod module shorturl go 1.15 require ( github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.4.2 github.com/zeromicro/go-zero v1.3.0 golang.org/x/net v0.0.0-20200707034311-ab3426394381 google.golang.org/grpc v1.29.1 ) 注意:这里可能存在 grpc 版本依赖的问题,可以用以上配置 5. 编写 API Gateway 代码 在 shorturl/api 目录下通过 goctl 生成 api/shorturl.api: $ goctl api -o shorturl.api 编辑 api/shorturl.api,为了简洁,去除了文件开头的 info,代码如下: type ( expandReq { shorten string `form:\"shorten\"` } expandResp { url string `json:\"url\"` } ) type ( shortenReq { url string `form:\"url\"` } shortenResp { shorten string `json:\"shorten\"` } ) service shorturl-api { @server( handler: ShortenHandler ) get /shorten(shortenReq) returns(shortenResp) @server( handler: ExpandHandler ) get /expand(expandReq) returns(expandResp) } type 用法和 go 一致,service 用来定义 get/post/head/delete 等 api 请求,解释如下: service shorturl-api { 这一行定义了 service 名字 @server 部分用来定义 server 端用到的属性 handler 定义了服务端 handler 名字 get /shorten(shortenReq) returns(shortenResp) 定义了 get 方法的路由、请求参数、返回参数等 使用 goctl 生成 API Gateway 代码 $ goctl api go -api shorturl.api -dir . 生成的文件结构如下: . ├── api │ ├── etc │ │ └── shorturl-api.yaml // 配置文件 │ ├── internal │ │ ├── config │ │ │ └── config.go // 定义配置 │ │ ├── handler │ │ │ ├── expandhandler.go // 实现 expandHandler │ │ │ ├── routes.go // 定义路由处理 │ │ │ └── shortenhandler.go // 实现 shortenHandler │ │ ├── logic │ │ │ ├── expandlogic.go // 实现 ExpandLogic │ │ │ └── shortenlogic.go // 实现 ShortenLogic │ │ ├── svc │ │ │ └── servicecontext.go // 定义 ServiceContext │ │ └── types │ │ └── types.go // 定义请求、返回结构体 │ ├── shorturl.api │ └── shorturl.go // main 入口定义 ├── go.mod └── go.sum 启动 API Gateway 服务,默认侦听在 8888 端口 $ go run shorturl.go -f etc/shorturl-api.yaml 测试 API Gateway 服务 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 27 Aug 2020 14:31:39 GMT Content-Length: 15 {\"shorten\":\"\"} 可以看到我们 API Gateway 其实啥也没干,就返回了个空值,接下来我们会在 rpc 服务里实现业务逻辑 可以修改 internal/svc/servicecontext.go 来传递服务依赖(如果需要) 实现逻辑可以修改 internal/logic 下的对应文件 可以通过 goctl 生成各种客户端语言的 api 调用代码 到这里,你已经可以通过 goctl 生成客户端代码给客户端同学并行开发了,支持多种语言,详见文档 6. 编写 transform rpc 服务 在 shorturl 目录下创建 rpc 目录 在 rpc/transform 目录下编写 transform.proto 文件 可以通过命令生成 proto 文件模板 $ goctl rpc template -o transform.proto 修改后文件内容如下: syntax = \"proto3\"; package transform; message expandReq { string shorten = 1; } message expandResp { string url = 1; } message shortenReq { string url = 1; } message shortenResp { string shorten = 1; } service transformer { rpc expand(expandReq) returns(expandResp); rpc shorten(shortenReq) returns(shortenResp); } 用 goctl 生成 rpc 代码,在 rpc/transform 目录下执行命令 $ goctl rpc proto -src transform.proto -dir . 注意:不能在 GOPATH 目录下执行以上命令 文件结构如下: rpc/transform ├── etc │ └── transform.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ ├── expandlogic.go // expand 业务逻辑在这里实现 │ │ └── shortenlogic.go // shorten 业务逻辑在这里实现 │ ├── server │ │ └── transformerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义 ServiceContext,传递依赖 ├── pb │ └── transform.pb.go ├── transform.go // rpc 服务 main 函数 ├── transform.proto └── transformer ├── transformer.go // 提供了外部调用方法,无需修改 ├── transformer_mock.go // mock 方法,测试用 └── types.go // request/response 结构体定义 直接可以运行,如下: $ go run transform.go -f etc/transform.yaml Starting rpc server at 127.0.0.1:8080... 查看服务是否注册 $ ETCDCTL_API=3 etcdctl get transform.rpc --prefix transform.rpc/7587851893787585061 127.0.0.1:8080 etc/transform.yaml 文件里可以修改侦听端口等配置 7. 修改 API Gateway 代码调用 transform rpc 服务 修改配置文件 shorturl-api.yaml,增加如下内容 Transform: Etcd: Hosts: - localhost:2379 Key: transform.rpc 通过 etcd 自动去发现可用的 transform 服务 修改 internal/config/config.go 如下,增加 transform 服务依赖 type Config struct { rest.RestConf Transform zrpc.RpcClientConf // 手动代码 } 修改 internal/svc/servicecontext.go,如下: type ServiceContext struct { Config config.Config Transformer transformer.Transformer // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Transformer: transformer.NewTransformer(zrpc.MustNewClient(c.Transform)), // 手动代码 } } 通过 ServiceContext 在不同业务逻辑之间传递依赖 修改 internal/logic/expandlogic.go 里的 Expand 方法,如下: func (l *ExpandLogic) Expand(req types.ExpandReq) (types.ExpandResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Expand(l.ctx, &transformer.ExpandReq{ Shorten: req.Shorten, }) if err != nil { return types.ExpandResp{}, err } return types.ExpandResp{ Url: resp.Url, }, nil // 手动代码结束 } 通过调用 transformer 的 Expand 方法实现短链恢复到 url 修改 internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(req types.ShortenReq) (types.ShortenResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Shorten(l.ctx, &transformer.ShortenReq{ Url: req.Url, }) if err != nil { return types.ShortenResp{}, err } return types.ShortenResp{ Shorten: resp.Shorten, }, nil // 手动代码结束 } 有的版本生成返回值可能是指针类型,需要自己调整下 通过调用 transformer 的 Shorten 方法实现 url 到短链的变换 至此,API Gateway 修改完成,虽然贴的代码多,但是其中修改的是很少的一部分,为了方便理解上下文,我贴了完整代码,接下来处理 CRUD+cache 8. 定义数据库表结构,并生成 CRUD+cache 代码 shorturl 下创建 rpc/transform/model 目录:mkdir -p rpc/transform/model 在 rpc/transform/model 目录下编写创建 shorturl 表的 sql 文件 shorturl.sql,如下: CREATE TABLE `shorturl` ( `shorten` varchar(255) NOT NULL COMMENT 'shorten key', `url` varchar(255) NOT NULL COMMENT 'original url', PRIMARY KEY(`shorten`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 创建 DB 和 table create database gozero; source shorturl.sql; 在 rpc/transform/model 目录下执行如下命令生成 CRUD+cache 代码,-c 表示使用 redis cache $ goctl model mysql ddl -c -src shorturl.sql -dir . 也可以用 datasource 命令代替 ddl 来指定数据库链接直接从 schema 生成 生成后的文件结构如下: Plain Text rpc/transform/model ├── shorturl.sql ├── shorturlmodel.go // CRUD+cache 代码 └── vars.go // 定义常量和变量 9. 修改 shorten/expand rpc 代码调用 crud+cache 代码 修改 rpc/transform/etc/transform.yaml,增加如下内容: DataSource: root:password@tcp(localhost:3306)/gozero Table: shorturl Cache: - Host: localhost:6379 可以使用多个 redis 作为 cache,支持 redis 单点或者 redis 集群 修改 rpc/transform/internal/config/config.go,如下: type Config struct { zrpc.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 } 增加了 mysql 和 redis cache 配置 修改 rpc/transform/internal/svc/servicecontext.go,如下: type ServiceContext struct { c config.Config Model model.ShorturlModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache), // 手动代码 } } 修改 rpc/transform/internal/logic/expandlogic.go,如下: func (l *ExpandLogic) Expand(in *transform.ExpandReq) (*transform.ExpandResp, error) { // 手动代码开始 res, err := l.svcCtx.Model.FindOne(l.ctx, in.Shorten) if err != nil { return nil, err } return &transform.ExpandResp{ Url: res.Url, }, nil // 手动代码结束 } 修改 rpc/transform/internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(in *transform.ShortenReq) (*transform.ShortenResp, error) { // 手动代码开始,生成短链接 key := hash.Md5Hex([]byte(in.Url))[:6] object, _ := l.svcCtx.Model.FindOne(l.ctx, key) if object != nil { return &transform.ShortenResp{ Shorten: key, }, nil } _, err := l.svcCtx.Model.Insert(l.ctx, &model.Shorturl{ Shorten: key, Url: in.Url, }) if err != nil { return nil, err } return &transform.ShortenResp{ Shorten: key, }, nil // 手动代码结束 } 至此代码修改完成,凡是手动修改的代码我加了标注 注意: undefined cache,你需要 import \"github.com/zeromicro/go-zero/core/stores/cache\" undefined model, sqlx, hash 等,你需要在文件中 import \"shorturl/rpc/transform/model\" import \"github.com/zeromicro/go-zero/core/stores/sqlx\" 10. 完整调用演示 shorten api 调用 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:49:49 GMT Content-Length: 21 {\"shorten\":\"f35b2a\"} expand api 调用 $ curl -i \"http://localhost:8888/expand?shorten=f35b2a\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:51:53 GMT Content-Length: 34 {\"url\":\"http://www.xiaoheiban.cn\"} 11. Benchmark 因为写入依赖于 mysql 的写入速度,就相当于压 mysql 了,所以压测只测试了 expand 接口,相当于从 mysql 里读取并利用缓存,shorten.lua 里随机从 db 里获取了 100 个热 key 来生成压测请求 可以看出在我的 MacBook Pro 上能达到 3 万 + 的 qps。 12. 完整代码 https://github.com/zeromicro/zero-examples/tree/main/shorturl 12. 总结 我们一直强调 工具大于约定和文档。 go-zero 不只是一个框架,更是一个建立在框架 + 工具基础上的,简化和规范了整个微服务构建的技术体系。 我们在保持简单的同时也尽可能把微服务治理的复杂度封装到了框架内部,极大的降低了开发人员的心智负担,使得业务开发得以快速推进。 通过 go-zero+goctl 生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。 有任何好的提升工程效率的想法,随时欢迎交流!👏 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"breaker-algorithms.html":{"url":"breaker-algorithms.html","title":"熔断原理与实现","keywords":"","body":"熔断原理与实现 在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机 由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断 熔断器原理 熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误 在这种模式下,服务调用方为每一个调用服务(调用路径)维护一个状态机,在这个状态机中有三个状态: 关闭(Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态 打开(Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复 半打开(Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到打开状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮 服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回 熔断器引入 上面介绍了熔断器的原理,在了解完原理后,你是否有思考我们如何引入熔断器呢?一种方案是在业务逻辑中可以加入熔断器,但显然是不够优雅也不够通用的,因此我们需要把熔断器集成在框架内,在zRPC框架内就内置了熔断器 我们知道,熔断器主要是用来保护调用端,调用端在发起请求的时候需要先经过熔断器,而客户端拦截器正好兼具了这个这个功能,所以在zRPC框架内熔断器是实现在客户端拦截器内,拦截器的原理如下图: 对应的代码为: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // 基于请求方法进行熔断 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正发起调用 return invoker(ctx, method, req, reply, cc, opts...) // codes.Acceptable判断哪种错误需要加入熔断错误计数 }, codes.Acceptable) } 熔断器实现 zRPC中熔断器的实现参考了Google Sre过载保护算法,该算法的原理如下: 请求数量(requests):调用方发起请求的数量总和 请求接受数量(accepts):被调用方正常处理的请求数量 在正常情况下,这两个值是相等的,随着被调用方服务出现异常开始拒绝请求,请求接受数量(accepts)的值开始逐渐小于请求数量(requests),这个时候调用方可以继续发送请求,直到requests = K * accepts,一旦超过这个限制,熔断器就回打开,新的请求会在本地以一定的概率被抛弃直接返回错误,概率的计算公式如下: 通过修改算法中的K(倍值),可以调节熔断器的敏感度,当降低该倍值会使自适应熔断算法更敏感,当增加该倍值会使得自适应熔断算法降低敏感度,举例来说,假设将调用方的请求上限从 requests = 2 acceptst 调整为 requests = 1.1 accepts 那么就意味着调用方每十个请求之中就有一个请求会触发熔断 代码路径为go-zero/core/breaker type googleBreaker struct { k float64 // 倍值 默认1.5 stat *collection.RollingWindow // 滑动时间窗口,用来对请求失败和成功计数 proba *mathx.Proba // 动态概率 } 自适应熔断算法实现 func (b *googleBreaker) accept() error { accepts, total := b.history() // 请求接受数量和请求总量 weightedAccepts := b.k * float64(accepts) // 计算丢弃请求概率 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio 每次发起请求会调用doReq方法,在这个方法中首先通过accept效验是否触发熔断,acceptable用来判断哪些error会计入失败计数,定义如下: func Acceptable(err error) bool { switch status.Code(err) { case codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss: // 异常请求错误 return false default: return true } } 如果请求正常则通过markSuccess把请求数量和请求接受数量都加一,如果请求不正常则只有请求数量会加一 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { // 判断是否触发熔断 if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 执行真正的调用 err := req() // 正常请求计数 if acceptable(err) { b.markSuccess() } else { // 异常请求计数 b.markFailure() } return err } 总结 调用端可以通过熔断机制进行自我保护,防止调用下游服务出现异常,或者耗时过长影响调用端的业务逻辑,很多功能完整的微服务框架都会内置熔断器。其实,不仅微服务调用之间需要熔断器,在调用依赖资源的时候,比如mysql、redis等也可以引入熔断器的机制。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"collection.html":{"url":"collection.html","title":"进程内缓存组件 collection.Cache","keywords":"","body":"通过 collection.Cache 进行缓存 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 进程内缓存工具collection.Cache 在做服务器开发的时候,相信都会遇到使用缓存的情况,go-zero 提供的简单的缓存封装 collection.Cache,简单使用方式如下 // 初始化 cache,其中 WithLimit 可以指定最大缓存的数量 c, err := collection.NewCache(time.Minute, collection.WithLimit(10000)) if err != nil { panic(err) } // 设置缓存 c.Set(\"key\", user) // 获取缓存,ok:是否存在 v, ok := c.Get(\"key\") // 删除缓存 c.Del(\"key\") // 获取缓存,如果 key 不存在的,则会调用 func 去生成缓存 v, err := c.Take(\"key\", func() (interface{}, error) { return user, nil }) cache 实现的建的功能包括 缓存自动失效,可以指定过期时间 缓存大小限制,可以指定缓存个数 缓存增删改 缓存命中率统计 并发安全 缓存击穿 实现原理: Cache 自动失效,是采用 TimingWheel(https://github.com/zeromicro/zeromicro/blob/master/core/collection/timingwheel.go) 进行管理的 timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) Cache 大小限制,是采用 LRU 淘汰策略,在新增缓存的时候会去检查是否已经超出过限制,具体代码在 keyLru 中实现 func (klru *keyLru) add(key string) { if elem, ok := klru.elements[key]; ok { klru.evicts.MoveToFront(elem) return } // Add new item elem := klru.evicts.PushFront(key) klru.elements[key] = elem // Verify size not exceeded if klru.evicts.Len() > klru.limit { klru.removeOldest() } } Cache 的命中率统计,是在代码中实现 cacheStat,在缓存命中丢失的时候自动统计,并且会定时打印使用的命中率, qps 等状态. 打印的具体效果如下 cache(proc) - qpm: 2, hit_ratio: 50.0%, elements: 0, hit: 1, miss: 1 缓存击穿包含是使用 syncx.SingleFlight(https://github.com/zeromicro/zeromicro/blob/master/core/syncx/singleflight.go) 进行实现的,就是将同时请求同一个 key 的请求, 关于 SingleFlight 后续会继续补充。 相关具体实现是在: func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { v, e := fetch() if e != nil { return nil, e } c.Set(key, v) return v, nil }) if err != nil { return nil, err } if fresh { c.stats.IncrementMiss() return val, nil } else { // got the result from previous ongoing query c.stats.IncrementHit() } return val, nil } 本文主要介绍了go-zero框架中的 Cache 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"keywords.html":{"url":"keywords.html","title":"高效的关键词替换和敏感词过滤工具","keywords":"","body":"高效的关键词替换和敏感词过滤工具 1. 算法介绍 利用高效的Trie树建立关键词树,如下图所示,然后依次查找字符串中的相连字符是否形成树的一条路径 发现掘金上这篇文章写的比较详细,可以一读,具体原理在此不详述。 2. 关键词替换 支持关键词重叠,自动选用最短的关键词,并且只会对原始字符串只会遍历一次进行匹配替换,如果替换结果中又出现的关键词不会被二次替换(如果业务上有这种可能性,请自行对上一次的替换结果再次执行替换操作),代码示例如下: replacer := stringx.NewReplacer(map[string]string{ \"日本\": \"法国\", \"日本的首都\": \"东京\", \"东京\": \"日本的首都\", }) fmt.Println(replacer.Replace(\"日本的首都是东京\")) 可以得到: ```Plain Text 法国的首都是日本的首都 示例代码见`stringx/replace/replace.go` ## 3. 查找敏感词 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }) keywords := filter.FindKeywords(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(keywords) 可以得到: ```Plain Text [苍井空 日本AV女优 AV演员色情 AV AV演员] ## 4. 敏感词过滤 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }, stringx.WithMask('?')) // 默认替换为* safe, keywords, found := filter.Filter(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(safe) fmt.Println(keywords) fmt.Println(found) 可以得到: Plain Text 日本????兼电视、电影演员。?????女优是xx出道, ??????们最精彩的表演是??????表演 [苍井空 日本AV女优 AV演员色情 AV AV演员] true 示例代码见stringx/filter/filter.go 5. Benchmark Sentences Keywords Regex go-zero 10000 10000 16min10s 27.2ms Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"loadshedding.html":{"url":"loadshedding.html","title":"服务自适应降载保护设计","keywords":"","body":"服务自适应降载保护设计 设计目的 保证系统不被过量请求拖垮 在保证系统稳定的前提下,尽可能提供更高的吞吐量 设计考虑因素 如何衡量系统负载 是否处于虚机或容器内,需要读取cgroup相关负载 用1000m表示100%CPU,推荐使用800m表示系统高负载 尽可能小的Overhead,不显著增加RT 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决 机制设计 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定 当CPU负载大于指定值时触发降载保护机制 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time) 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个 满足以下所有条件则拒绝该请求 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动 averageFlying > max(1, QPS*minRT/1e3) averageFlying = MovingAverage(flying) 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值 取flying值的时候,有三种做法: 请求增加后更新一次averageFlying,见图中橙色曲线 请求结束后更新一次averageFlying,见图中绿色曲线 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying 我们使用的是第二种,这样可以更好的防止抖动,如图: QPS = maxPass * bucketsPerSecond maxPass表示每个有效桶里的成功的requests bucketsPerSecond表示每秒有多少个桶 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求 降载的使用 已经在rest和zrpc框架里增加了可选激活配置 CpuThreshold,如果把值设置为大于0的值,则激活该服务的自动降载机制 如果请求被drop,那么错误日志里会有dropreq关键字 参考资料 滑动平均 Sentinel自适应限流 Kratos自适应限流保护 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mapping.html":{"url":"mapping.html","title":"文本序列化和反序列化","keywords":"","body":"文本序列化和反序列化 go-zero针对文本的序列化和反序列化主要在三个地方使用 http api请求体的反序列化 http api返回体的序列化 配置文件的反序列化 本文假定读者已经定义过api文件以及修改过配置文件,如不熟悉,可参照 快速构建高并发微服务 快速构建高并发微服务 1. http api请求体的反序列化 在反序列化的过程中的针对请求数据的数据格式以及数据校验需求,go-zero实现了自己的一套反序列化机制 1.1 数据格式以订单order.api文件为例 type ( createOrderReq struct { token string `path:\"token\"` // 用户token productId string `json:\"productId\"` // 商品ID num int `json:\"num\"` // 商品数量 } createOrderRes struct { success bool `json:\"success\"` // 是否成功 } findOrderReq struct { token string `path:\"token\"` // 用户token page int `form:\"page\"` // 页数 pageSize int8 `form:\"pageSize\"` // 页大小 } findOrderRes struct { orderInfo []orderInfo `json:\"orderInfo\"` // 商品ID } orderInfo struct { productId string `json:\"productId\"` // 商品ID productName string `json:\"productName\"` // 商品名称 num int `json:\"num\"` // 商品数量 } deleteOrderReq struct { id string `path:\"id\"` } deleteOrderRes struct { success bool `json:\"success\"` // 是否成功 } ) service order { @doc( summary: 创建订单 ) @handler CreateOrderHandler post /order/add/:token(createOrderReq) returns(createOrderRes) @doc( summary: 获取订单 ) @handler FindOrderHandler get /order/find/:token(findOrderReq) returns(findOrderRes) @doc( summary: 删除订单 ) @handler: DeleteOrderHandler delete /order/:id(deleteOrderReq) returns(deleteOrderRes) } http api请求体的反序列化的tag有三种: path:http url 路径中参数反序列化 /order/add/1234567会解析出来token为1234567 form:http form表单反序列化,需要 header头添加 Content-Type: multipart/form-data /order/find/1234567?page=1&pageSize=20会解析出来token为1234567,page为1,pageSize为20 json:http request json body反序列化,需要 header头添加 Content-Type: application/json {\"productId\":\"321\",\"num\":1}会解析出来productId为321,num为1 1.2 数据校验以用户user.api文件为例 type ( createUserReq struct { age int8 `json:\"age,default=20,range=(12:100]\"` // 年龄 name string `json:\"name\"` // 名字 alias string `json:\"alias,optional\"` // 别名 sex string `json:\"sex,options=male|female\"` // 性别 avatar string `json:\"avatar,default=default.png\"` // 头像 } createUserRes struct { success bool `json:\"success\"` // 是否成功 } ) service user { @doc( summary: 创建订单 ) @handler CreateUserHandler post /user/add(createUserReq) returns(createUserRes) } 数据校验有很多种方式,包括以下但不限: age:默认不输入为20,输入则取值范围为(12:100],前开后闭 name:必填,不可为空 alias:选填,可为空 sex:必填,取值为male或female avatar:选填,默认为default.png 更多详情参见unmarshaler_test.go 2. http api返回体的序列化 使用官方默认的encoding/json包序列化,在此不再累赘 3. 配置文件的反序列化 配置文件的反序列化和http api请求体的反序列化使用同一套解析规则,可参照http api请求体的反序列化 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"mapreduce.html":{"url":"mapreduce.html","title":"并发处理工具 MapReduce","keywords":"","body":"通过MapReduce降低服务响应时间 在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了 WaitGroup 工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错 基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,该工具开箱即用,不需要做什么初始化,我们通过下图看下使用MapReduce和没使用的耗时对比: 相同的依赖,串行处理的话需要200ms,使用MapReduce后的耗时等于所有依赖中最大的耗时为100ms,可见MapReduce可以大大降低服务耗时,而且随着依赖的增加效果就会越明显,减少处理耗时的同时并不会增加服务器压力 并发处理工具MapReduce MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想 go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能 我们通过几个示例来演示MapReduce的用法 MapReduce主要有三个参数,第一个参数为generate用以生产数据,第二个参数为mapper用以对数据进行处理,第三个参数为reducer用以对mapper后的数据做聚合返回,还可以通过opts选项设置并发处理的线程数量 场景一: 某些功能的结果往往需要依赖多个服务,比如商品详情的结果往往会依赖用户服务、库存服务、订单服务等等,一般被依赖的服务都是以rpc的形式对外提供,为了降低依赖的耗时我们往往需要对依赖做并行处理 func productDetail(uid, pid int64) (*ProductDetail, error) { var pd ProductDetail err := mr.Finish(func() (err error) { pd.User, err = userRpc.User(uid) return }, func() (err error) { pd.Store, err = storeRpc.Store(pid) return }, func() (err error) { pd.Order, err = orderRpc.Order(pid) return }) if err != nil { log.Printf(\"product detail error: %v\", err) return nil, err } return &pd, nil } 该示例中返回商品详情依赖了多个服务获取数据,因此做并发的依赖处理,对接口的性能有很大的提升 场景二: 很多时候我们需要对一批数据进行处理,比如对一批用户id,效验每个用户的合法性并且效验过程中有一个出错就认为效验失败,返回的结果为效验合法的用户id func checkLegal(uids []int64) ([]int64, error) { r, err := mr.MapReduce(func(source chan 该示例中,如果check过程出现错误则通过cancel方法结束效验过程,并返回error整个效验过程结束,如果某个uid效验结果为false则最终结果不返回该uid MapReduce使用注意事项 mapper和reducer中都可以调用cancel,参数为error,调用后立即返回,返回结果为nil, error mapper中如果不调用writer.Write则item最终不会被reducer聚合 reducer中如果不调用writer.Wirte则返回结果为nil, ErrReduceNoOutput reducer为单线程,所有mapper出来的结果在这里串行聚合 实现原理分析: MapReduce中首先通过buildSource方法通过执行generate(参数为无缓冲channel)产生数据,并返回无缓冲的channel,mapper会从该channel中读取数据 func buildSource(generate GenerateFunc) chan interface{} { source := make(chan interface{}) go func() { defer close(source) generate(source) }() return source } 在MapReduceWithSource方法中定义了cancel方法,mapper和reducer中都可以调用该方法,调用后主线程收到close信号会立马返回 cancel := once(func(err error) { if err != nil { retErr.Set(err) } else { // 默认的error retErr.Set(ErrCancelWithNil) } drain(source) // 调用close(ouput)主线程收到Done信号,立马返回 finish() }) 在mapperDispatcher方法中调用了executeMappers,executeMappers消费buildSource产生的数据,每一个item都会起一个goroutine单独处理,默认最大并发数为16,可以通过WithWorkers进行设置 var wg sync.WaitGroup defer func() { wg.Wait() // 保证所有的item都处理完成 close(collector) }() pool := make(chan lang.PlaceholderType, workers) writer := newGuardedWriter(collector, done) // 将mapper处理完的数据写入collector for { select { case reducer单goroutine对数mapper写入collector的数据进行处理,如果reducer中没有手动调用writer.Write则最终会执行finish方法对output进行close避免死锁 go func() { defer func() { if r := recover(); r != nil { cancel(fmt.Errorf(\"%v\", r)) } else { finish() } }() reducer(collector, writer, cancel) }() 在该工具包中还提供了许多针对不同业务场景的方法,实现原理与MapReduce大同小异,感兴趣的同学可以查看源码学习 MapReduceVoid 功能和MapReduce类似但没有结果返回只返回error Finish 处理固定数量的依赖,返回error,有一个error立即返回 FinishVoid 和Finish方法功能类似,没有返回值 Map 只做generate和mapper处理,返回channel MapVoid 和Map功能类似,无返回 本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"metric.html":{"url":"metric.html","title":"基于prometheus的微服务指标监控","keywords":"","body":"基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero中目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"sharedcalls.html":{"url":"sharedcalls.html","title":"防止缓存击穿之进程内共享调用","keywords":"","body":"防止缓存击穿之进程内共享调用 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等。 本文主要讲述进程内共享调用神器SharedCalls(v1.2.0改名为SingleFlight ) 使用场景 并发场景下,可能会有多个线程(协程)同时请求同一份资源,如果每个请求都要走一遍资源的请求过程,除了比较低效之外,还会对资源服务造成并发的压力。举一个具体例子,比如缓存失效,多个请求同时到达某服务请求某资源,该资源在缓存中已经失效,此时这些请求会继续访问DB做查询,会引起数据库压力瞬间增大。而使用SharedCalls可以使得同时多个请求只需要发起一次拿结果的调用,其他请求\"坐享其成\",这种设计有效减少了资源服务的并发压力,可以有效防止缓存击穿。 高并发场景下,当某个热点key缓存失效后,多个请求会同时从数据库加载该资源,并保存到缓存,如果不做防范,可能会导致数据库被直接打死。针对这种场景,go-zero框架中已经提供了实现,具体可参看sqlc和mongoc等实现代码。 为了简化演示代码,我们通过多个线程同时去获取一个id来模拟缓存的场景。如下: func main() { const round = 5 var wg sync.WaitGroup barrier := syncx.NewSharedCalls() wg.Add(round) for i := 0; i 运行,打印结果为: 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 可以看出,只要是同一个key上的同时发起的请求,都会共享同一个结果,对获取DB数据进缓存等场景特别有用,可以有效防止缓存击穿。 关键源码分析 SharedCalls interface提供了Do和DoEx两种方法的抽象 // SharedCalls接口提供了Do和DoEx两种方法 type SharedCalls interface { Do(key string, fn func() (interface{}, error)) (interface{}, error) DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error) } SharedCalls interface的具体实现sharedGroup // call代表对指定资源的一次请求 type call struct { wg sync.WaitGroup // 用于协调各个请求goroutine之间的资源共享 val interface{} // 用于保存请求的返回值 err error // 用于保存请求过程中发生的错误 } type sharedGroup struct { calls map[string]*call lock sync.Mutex } sharedGroup的Do方法 key参数:可以理解为资源的唯一标识。 fn参数:真正获取资源的方法。 处理过程分析: // 当多个请求同时使用Do方法请求资源时 func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) { // 先申请加锁 g.lock.Lock() // 根据key,获取对应的call结果,并用变量c保存 if c, ok := g.calls[key]; ok { // 拿到call以后,释放锁,此处call可能还没有实际数据,只是一个空的内存占位 g.lock.Unlock() // 调用wg.Wait,判断是否有其他goroutine正在申请资源,如果阻塞,说明有其他goroutine正在获取资源 c.wg.Wait() // 当wg.Wait不再阻塞,表示资源获取已经结束,可以直接返回结果 return c.val, c.err } // 没有拿到结果,则调用makeCall方法去获取资源,注意此处仍然是锁住的,可以保证只有一个goroutine可以调用makecall c := g.makeCall(key, fn) // 返回调用结果 return c.val, c.err } sharedGroup的DoEx方法 和Do方法类似,只是返回值中增加了布尔值表示值是调用makeCall方法直接获取的,还是取的共享成果 func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) { g.lock.Lock() if c, ok := g.calls[key]; ok { g.lock.Unlock() c.wg.Wait() return c.val, false, c.err } c := g.makeCall(key, fn) return c.val, true, c.err } sharedGroup的makeCall方法 该方法由Do和DoEx方法调用,是真正发起资源请求的方法。 // 进入makeCall的一定只有一个goroutine,因为要拿锁锁住的 func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call { // 创建call结构,用于保存本次请求的结果 c := new(call) // wg加1,用于通知其他请求资源的goroutine等待本次资源获取的结束 c.wg.Add(1) // 将用于保存结果的call放入map中,以供其他goroutine获取 g.calls[key] = c // 释放锁,这样其他请求的goroutine才能获取call的内存占位 g.lock.Unlock() defer func() { // delete key first, done later. can't reverse the order, because if reverse, // another Do call might wg.Wait() without get notified with wg.Done() g.lock.Lock() delete(g.calls, key) g.lock.Unlock() // 调用wg.Done,通知其他goroutine可以返回结果,这样本批次所有请求完成结果的共享 c.wg.Done() }() // 调用fn方法,将结果填入变量c中 c.val, c.err = fn() return c } 最后 本文主要介绍了go-zero框架中的 SharedCalls工具,对其应用场景和关键代码做了简单的梳理,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"sql-cache.html":{"url":"sql-cache.html","title":"DB缓存机制","keywords":"","body":"DB缓存机制 QueryRowIndex 没有查询条件到Primary映射的缓存 通过查询条件到DB去查询行记录,然后 把Primary到行记录的缓存写到redis里 把查询条件到Primary的映射保存到redis里,框架的Take方法自动做了 可能的过期顺序 查询条件到Primary的映射缓存未过期 Primary到行记录的缓存未过期 直接返回缓存行记录 Primary到行记录的缓存已过期 通过Primary到DB获取行记录,并写入缓存 此时存在的问题是,查询条件到Primary的缓存可能已经快要过期了,短时间内的查询又会触发一次数据库查询 要避免这个问题,可以让上面粗体部分第一个过期时间略长于第二个,比如5秒 查询条件到Primary的映射缓存已过期,不管Primary到行记录的缓存是否过期 查询条件到Primary的映射会被重新获取,获取过程中会自动写入新的Primary到行记录的缓存,这样两种缓存的过期时间都是刚刚设置 有查询条件到Primary映射的缓存 没有Primary到行记录的缓存 通过Primary到DB查询行记录,并写入缓存 有Primary到行记录的缓存 直接返回缓存结果 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"zrpc.html":{"url":"zrpc.html","title":"zrpc 使用介绍","keywords":"","body":"企业级RPC框架zRPC 近期比较火的开源项目go-zero是一个集成了各种工程实践的包含了Web和RPC协议的功能完善的微服务框架,今天我们就一起来分析一下其中的RPC部分zRPC。 zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。 zRPC初探 zRPC支持直连和基于etcd服务发现两种方式,我们以基于etcd做服务发现为例演示zRPC的基本使用: 配置 创建hello.yaml配置文件,配置如下: Name: hello.rpc // 服务名 ListenOn: 127.0.0.1:9090 // 服务监听地址 Etcd: Hosts: - 127.0.0.1:2379 // etcd服务地址 Key: hello.rpc // 服务注册key 创建proto文件 创建hello.proto文件,并生成对应的go代码 syntax = \"proto3\"; package pb; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 生成go代码 protoc --go_out=plugins=grpc:. hello.proto Server端 package main import ( \"context\" \"flag\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type Config struct { zrpc.RpcServerConf } var cfgFile = flag.String(\"f\", \"./hello.yaml\", \"cfg file\") func main() { flag.Parse() var cfg Config conf.MustLoad(*cfgFile, &cfg) srv, err := zrpc.NewServer(cfg.RpcServerConf, func(s *grpc.Server) { pb.RegisterGreeterServer(s, &Hello{}) }) if err != nil { log.Fatal(err) } srv.Start() } type Hello struct{} func (h *Hello) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: \"hello \" + in.Name}, nil } Client端 package main import ( \"context\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/discov\" \"github.com/zeromicro/go-zero/zrpc\" ) func main() { client := zrpc.MustNewClient(zrpc.RpcClientConf{ Etcd: discov.EtcdConf{ Hosts: []string{\"127.0.0.1:2379\"}, Key: \"hello.rpc\", }, }) conn := client.Conn() hello := pb.NewGreeterClient(conn) reply, err := hello.SayHello(context.Background(), &pb.HelloRequest{Name: \"go-zero\"}) if err != nil { log.Fatal(err) } log.Println(reply.Message) } 启动服务,查看服务是否注册: ETCDCTL_API=3 etcdctl get hello.rpc --prefix 显示服务已经注册: hello.rpc/7587849401504590084 127.0.0.1:9090 运行客户端即可看到输出: hello go-zero 这个例子演示了zRPC的基本使用,可以看到通过zRPC构建RPC服务非常简单,只需要很少的几行代码,接下来我们继续进行探索 zRPC原理分析 下图展示zRPC的架构图和主要组成部分 zRPC主要有以下几个模块组成: discov: 服务发现模块,基于etcd实现服务发现功能 resolver: 服务注册模块,实现了gRPC的resolver.Builder接口并注册到gRPC interceptor: 拦截器,对请求和响应进行拦截处理 balancer: 负载均衡模块,实现了p2c负载均衡算法,并注册到gRPC client: zRPC客户端,负责发起请求 server: zRPC服务端,负责处理请求 这里介绍了zRPC的主要组成模块和每个模块的主要功能,其中resolver和balancer模块实现了gRPC开放的接口,实现了自定义的resolver和balancer,拦截器模块是整个zRPC的功能重点,自适应降载、自适应熔断、prometheus服务指标收集等功能都在这里实现 Interceptor模块 gRPC提供了拦截器功能,主要是对请求前后进行额外处理的拦截操作,其中拦截器包含客户端拦截器和服务端拦截器,又分为一元(Unary)拦截器和流(Stream)拦截器,这里我们主要讲解一元拦截器,流拦截器同理。 客户端拦截器定义如下: type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error 其中method为方法名,req,reply分别为请求和响应参数,cc为客户端连接对象,invoker参数是真正执行rpc方法的handler其实在拦截器中被调用执行 服务端拦截器定义如下: type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) 其中req为请求参数,info中包含了请求方法属性,handler为对server端方法的包装,也是在拦截器中被调用执行 zRPC中内置了丰富的拦截器,其中包括自适应降载、自适应熔断、权限验证、prometheus指标收集等等,由于拦截器较多,篇幅有限没法所有的拦截器给大家一一解析,这里我们主要分析两个,自适应熔断和prometheus服务监控指标收集: 内置拦截器分析 自适应熔断(breaker) 当客户端向服务端发起请求,客户端会记录服务端返回的错误,当错误达到一定的比例,客户端会自行的进行熔断处理,丢弃掉一定比例的请求以保护下游依赖,且可以自动恢复。zRPC中自适应熔断遵循《Google SRE》中过载保护策略,算法如下: requests: 总请求数量 accepts: 正常请求数量 K: 倍值 (Google SRE推荐值为2) 可以通过修改K的值来修改熔断发生的激进程度,降低K的值会使得自适应熔断算法更加激进,增加K的值则自适应熔断算法变得不再那么激进 熔断拦截器定义如下: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // target + 方法名 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正执行调用 return invoker(ctx, method, req, reply, cc, opts...) }, codes.Acceptable) } accept方法实现了Google SRE过载保护算法,判断否进行熔断 func (b *googleBreaker) accept() error { // accepts为正常请求数,total为总请求数 accepts, total := b.history() weightedAccepts := b.k * float64(accepts) // 算法实现 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio doReq方法首先判断是否熔断,满足条件直接返回error(circuit breaker is open),不满足条件则对请求数进行累加 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 此处执行RPC请求 err := req() // 正常请求total和accepts都会加1 if acceptable(err) { b.markSuccess() } else { // 请求失败只有total会加1 b.markFailure() } return err } prometheus指标收集 服务监控是了解服务当前运行状态以及变化趋势的重要手段,监控依赖于服务指标的收集,通过prometheus进行监控指标的收集是业界主流方案,zRPC中也采用了prometheus来进行指标的收集 prometheus拦截器定义如下: 这个拦截器主要是对服务的监控指标进行收集,这里主要是对RPC方法的耗时和调用错误进行收集,这里主要使用了Prometheus的Histogram和Counter数据类型 func UnaryPrometheusInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) ( interface{}, error) { // 执行前记录一个时间 startTime := timex.Now() resp, err := handler(ctx, req) // 执行后通过Since算出执行该调用的耗时 metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), info.FullMethod) // 方法对应的错误码 metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err)))) return resp, err } } 添加自定义拦截器 除了内置了丰富的拦截器之外,zRPC同时支持添加自定义拦截器 Client端通过AddInterceptor方法添加一元拦截器: func (rc *RpcClient) AddInterceptor(interceptor grpc.UnaryClientInterceptor) { rc.client.AddInterceptor(interceptor) } Server端通过AddUnaryInterceptors方法添加一元拦截器: func (rs *RpcServer) AddUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) { rs.server.AddUnaryInterceptors(interceptors...) } resolver模块 zRPC服务注册架构图: zRPC中自定义了resolver模块,用来实现服务的注册功能。zRPC底层依赖gRPC,在gRPC中要想自定义resolver需要实现resolver.Builder接口: type Builder interface { Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error) Scheme() string } 其中Build方法返回Resolver,Resolver定义如下: type Resolver interface { ResolveNow(ResolveNowOptions) Close() } 在zRPC中定义了两种resolver,direct和discov,这里我们主要分析基于etcd做服务发现的discov,自定义的resolver需要通过gRPC提供了Register方法进行注册代码如下: func RegisterResolver() { resolver.Register(&dirBuilder) resolver.Register(&disBuilder) } 当我们启动我们的zRPC Server的时候,调用Start方法,会像etcd中注册对应的服务地址: func (ags keepAliveServer) Start(fn RegisterFn) error { // 注册服务地址 if err := ags.registerEtcd(); err != nil { return err } // 启动服务 return ags.Server.Start(fn) } 当我们启动zRPC客户端的时候,在gRPC内部会调用我们自定义resolver的Build方法,zRPC通过在Build方法内调用执行了resolver.ClientConn的UpdateState方法,该方法会把服务地址注册到gRPC客户端内部: func (d *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) ( resolver.Resolver, error) { hosts := strings.FieldsFunc(target.Authority, func(r rune) bool { return r == EndpointSepChar }) // 服务发现 sub, err := discov.NewSubscriber(hosts, target.Endpoint) if err != nil { return nil, err } update := func() { var addrs []resolver.Address for _, val := range subset(sub.Values(), subsetSize) { addrs = append(addrs, resolver.Address{ Addr: val, }) } // 向gRPC注册服务地址 cc.UpdateState(resolver.State{ Addresses: addrs, }) } // 监听 sub.AddListener(update) update() // 返回自定义的resolver.Resolver return &nopResolver{cc: cc}, nil } 在discov中,通过调用load方法从etcd中获取指定服务的所有地址: func (c *cluster) load(cli EtcdClient, key string) { var resp *clientv3.GetResponse for { var err error ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout) // 从etcd中获取指定服务的所有地址 resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix()) cancel() if err == nil { break } logx.Error(err) time.Sleep(coolDownInterval) } var kvs []KV c.lock.Lock() for _, ev := range resp.Kvs { kvs = append(kvs, KV{ Key: string(ev.Key), Val: string(ev.Value), }) } c.lock.Unlock() c.handleChanges(key, kvs) } 并通过watch监听服务地址的变化: func (c *cluster) watch(cli EtcdClient, key string) { rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix()) for { select { case wresp, ok := 这部分主要介绍了zRPC中是如何自定义的resolver,以及基于etcd的服务发现原理,通过这部分的介绍大家可以了解到zRPC内部服务注册发现的原理,源代码比较多只是粗略的从整个流程上进行了分析,如果大家对zRPC的源码比较感兴趣可以自行进行学习 balancer模块 负载均衡原理图: 避免过载是负载均衡策略的一个重要指标,好的负载均衡算法能很好的平衡服务端资源。常用的负载均衡算法有轮训、随机、Hash、加权轮训等。但为了应对各种复杂的场景,简单的负载均衡算法往往表现的不够好,比如轮训算法当服务响应时间变长就很容易导致负载不再平衡, 因此zRPC中自定义了默认负载均衡算法P2C(Power of Two Choices),和resolver类似,要想自定义balancer也需要实现gRPC定义的balancer.Builder接口,由于和resolver类似这里不再带大家一起分析如何自定义balancer,感兴趣的朋友可以查看gRPC相关的文档来进行学习 注意,zRPC是在客户端进行负载均衡,常见的还有通过nginx中间代理的方式 zRPC框架中默认的负载均衡算法为P2C,该算法的主要思想是: 从可用节点列表中做两次随机选择操作,得到节点A、B 比较A、B两个节点,选出负载最低的节点作为被选中的节点 伪代码如下: 主要算法逻辑在Pick方法中实现: func (p *p2cPicker) Pick(ctx context.Context, info balancer.PickInfo) ( conn balancer.SubConn, done func(balancer.DoneInfo), err error) { p.lock.Lock() defer p.lock.Unlock() var chosen *subConn switch len(p.conns) { case 0: return nil, nil, balancer.ErrNoSubConnAvailable case 1: chosen = p.choose(p.conns[0], nil) case 2: chosen = p.choose(p.conns[0], p.conns[1]) default: var node1, node2 *subConn for i := 0; i = a { b++ } // 随机获取所有节点中的两个节点 node1 = p.conns[a] node2 = p.conns[b] // 效验节点是否健康 if node1.healthy() && node2.healthy() { break } } // 选择其中一个节点 chosen = p.choose(node1, node2) } atomic.AddInt64(&chosen.inflight, 1) atomic.AddInt64(&chosen.requests, 1) return chosen.conn, p.buildDoneFunc(chosen), nil } choose方法对随机选择出来的节点进行负载比较从而最终确定选择哪个节点 func (p *p2cPicker) choose(c1, c2 *subConn) *subConn { start := int64(timex.Now()) if c2 == nil { atomic.StoreInt64(&c1.pick, start) return c1 } if c1.load() > c2.load() { c1, c2 = c2, c1 } pick := atomic.LoadInt64(&c2.pick) if start-pick > forcePick && atomic.CompareAndSwapInt64(&c2.pick, pick, start) { return c2 } else { atomic.StoreInt64(&c1.pick, start) return c1 } } 上面主要介绍了zRPC默认负载均衡算法的设计思想和代码实现,那自定义的balancer是如何注册到gRPC的呢,resolver提供了Register方法来进行注册,同样balancer也提供了Register方法来进行注册: func init() { balancer.Register(newBuilder()) } func newBuilder() balancer.Builder { return base.NewBalancerBuilder(Name, new(p2cPickerBuilder)) } 注册balancer之后gRPC怎么知道使用哪个balancer呢?这里我们需要使用配置项进行配置,在NewClient的时候通过grpc.WithBalancerName方法进行配置: func NewClient(target string, opts ...ClientOption) (*client, error) { var cli client opts = append(opts, WithDialOption(grpc.WithBalancerName(p2c.Name))) if err := cli.dial(target, opts...); err != nil { return nil, err } return &cli, nil } 这部分主要介绍了zRPC中内中的负载均衡算法的实现原理以及具体的实现方式,之后介绍了zRPC是如何注册自定义的balancer以及如何选择自定义的balancer,通过这部分大家应该对负载均衡有了更进一步的认识 总结 首先,介绍了zRPC的基本使用方法,可以看到zRPC使用非常简单,只需要少数几行代码就可以构建高性能和自带服务治理能力的RPC服务,当然这里没有面面俱到的介绍zRPC的基本使用,大家可以查看相关文档进行学习 接着,介绍了zRPC的几个重要组成模块以及其实现原理,并分析了部分源码。拦截器模块是整个zRPC的重点,其中内置了丰富的功能,像熔断、监控、降载等等也是构建高可用微服务必不可少的。resolver和balancer模块自定义了gRPC的resolver和balancer,通过该部分可以了解到整个服务注册与发现的原理以及如何构建自己的服务发现系统,同时自定义负载均衡算法也变得不再神秘 最后,zRPC是一个经历过各种工程实践的RPC框架,不论是想要用于生产还是学习其中的设计模式都是一个不可多得的开源项目。希望通过这篇文章的介绍大家能够进一步了解zRPC Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"go-zero-looklook.html":{"url":"go-zero-looklook.html","title":"使用go-zero开发一个旅游系统go-zero-looklook","keywords":"","body":"使用go-zero开发一个旅游系统go-zero-looklook 因为大家都在说目前go-zero没有一个完整的项目例子,本人接触go-zero可能比较早,go-zero在大约1000start左右我就在用了,后来跟go-zero作者加了微信也熟悉了,go-zero作者非常热心以及耐心的帮我解答了很多问题,我也想积极帮助go-zero推广社区,基本是在社区群内回答大家相关的问题,因为在这个过程中发现很多人觉得go-zero没有一个完整的项目例子,作为想推动社区的一员,索性我就把内部项目删减了一些关键东西,就搞了个可用版本开源出来,主要技术栈包含如下: go-zero nginx网关 filebeat kafka go-stash elasticsearch kibana prometheus grafana jaeger go-queue asynq asynqmon dtm docker docker-compose mysql redis 项目地址 https://github.com/Mikaelemmmm/go-zero-looklook 项目文档 https://github.com/Mikaelemmmm/go-zero-looklook/tree/main/doc 项目简介 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中间件,所用到的技术栈基本是go-zero项目组的自研组件,基本是go-zero全家桶了 另外,前端是小程序,本项目已经对接好了小程序授权登录 以及 微信支付了 ,前端看看后面是否能开源吧 项目目录结构如下: app:所有业务代码包含api、rpc以及mq(消息队列、延迟队列、定时任务) common:通用组件 error、middleware、interceptor、tool、ctxdata等 data:该项目包含该目录依赖所有中间件(mysql、es、redis、grafana等)产生的数据,此目录下的所有内容应该在git忽略文件中,不需要提交。 deploy: filebeat: docker部署filebeat配置 go-stash:go-stash配置 nginx: nginx网关配置 prometheus : prometheus配置 script: gencode:生成api、rpc,以及创建kafka语句,复制粘贴使用 mysql:生成model的sh工具 goctl: 该项目goctl的template,goctl生成自定义代码模版,tempalte用法可参考go-zero文档,复制到家目录下.goctl即可, 该项目用到goctl版本是v1.2.3 doc : 该项目系列文档 系统架构图 业务架构图 网关 nginx做网关,使用nginx的auth模块,调用后端的identity服务统一鉴权,业务内部不鉴权,如果涉及到业务资金比较多也可以在业务中进行二次鉴权,为了安全嘛。 另外,很多同学觉得nginx做网关不太好,这块原理基本一样,可以自行替换成apisix、kong等 开发模式 本项目使用的是微服务开发,api (http) + rpc(grpc) , api充当聚合服务,复杂、涉及到其他业务调用的统一写在rpc中,如果一些不会被其他服务依赖使用的简单业务,可以直接写在api的logic中 日志 关于日志,统一使用filebeat收集,上报到kafka中,由于logstash懂得都懂,资源占用太夸张了,这里使用了go-stash替换了logstash 链接:https://github.com/kevwan/go-stash , go-stash是由go-zero开发团队开发的,性能很高不占资源,主要代码量没多少,只需要配置就可以使用,很简单。它是吧kafka数据源同步到elasticsearch中,默认不支持elasticsearch账号密码,我fork了一份修改了一下,很简单支持了账号、密码 监控 监控采用prometheus,这个go-zero原生支持,只需要配置就可以了,这里可以看项目中的配置 链路追踪 go-zero默认jaeger、zipkin支持,只需要配置就可以了,可以看配置 消息队列 消息队列使用的是go-zero开发团队开发的go-queue,链接:https://github.com/zeromicro/go-queue 这里使用可kq,kq是基于kafka做的高性能消息队列 通用go-queue中也有dq,是延迟队列,不过当前项目没有使用dq 延迟队列、定时任务 延迟队列、定时任务本项目使用的是asynq , google团队给予redis开发的简单中间件, 当然了asynq也支持消息队列,你也可也把kq消息队列替换成这个,毕竟只需要redis不需要在去维护一个kafka也是不错的 链接:https://github.com/hibiken/asynq 分布式事务 分布式事务准备使用的是dtm, 嗯 ,很舒服,之前我写过一篇 \"go-zero对接分布式事务dtm保姆式教程\" 链接地址:https://github.com/Mikaelemmmm/gozerodtm , 本项目目前还未使用到,后续准备直接集成就好了,如果读者使用直接去看那个源码就行了 部署 部署的话,目前这个直接使用docker可以部署整套技术栈,如果上k8s的话 ,最简单直接用阿里云的吧 我说下思路,这个后续会出一个基于阿里云效的部署到k8s文档教程,自己搭建一个gitlab、jenkins、harbor去做的话太费时间了 1、将代码放在阿里云效(当然你整到gitlab也行) 2、在阿里云效创建流水线,基本是一个服务一个流水线了 3、流水线步骤 : ​ 拉取代码--->ci检测(这里可以省略哈,自己看着办)--->构建镜像(go-zero官方有Dockerfile还有教程,别告诉我不会)-->推送到阿里云镜像服务--->使用kubectl去阿里云k8s拉取镜像(ack、ask都行,ask无法使用daemonset 不能用filebeat)---->ok了 另外, 如果你想自己基于gitlab、jenkins、harbor去做的话,嗯 自己去找运维弄吧,我之前也写过一个教程,有空在整吧老哥们!! Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "},"faq.html":{"url":"faq.html","title":"FAQ","keywords":"","body":"常见问题集合 goctl安装了执行命令却提示 command not found: goctl 字样。 如果你通过 go get 方式安装,那么 goctl 应该位于 $GOPATH 中, 你可以通过 go env GOPATH 查看完整路径,不管你的 goctl 是在 $GOPATH中, 还是在其他目录,出现上述问题的原因就是 goctl 所在目录不在 PATH (环境变量)中所致。 rpc怎么调用 该问题可以参考快速开始中的rpc编写与调用介绍,其中有rpc调用的使用逻辑。 proto使用了import,goctl命令需要怎么写。 goctl 对于import的proto指定 BasePath 提供了 protoc 的flag映射,即 --proto_path, -I, goctl 会将此flag值传递给 protoc. 假设 base.proto 的被main proto 引入了,为什么不生能生成base.pb.go。 对于 base.proto 这种类型的文件,一般都是开发者有message复用的需求,他的来源不止有开发者自己编写的proto文件, 还有可能来源于 google.golang.org/grpc 中提供的一些基本的proto,比如 google/protobuf/any.proto, 如果由 goctl 来生成,那么就失去了集中管理这些proto的意义。 model怎么控制缓存时间 在 sqlc.NewNodeConn 的时候可以通过可选参数 cache.WithExpiry 传递,如缓存时间控制为1天,代码如下: sqlc.NewNodeConn(conn,redis,cache.WithExpiry(24*time.Hour)) jwt鉴权怎么实现 请参考jwt鉴权 api中间件怎么使用 请参考中间件 怎么关闭输出的统计日志(stat)? logx.DisableStat() rpc直连与服务发现连接模式写法 // mode1: 集群直连 // conf:=zrpc.NewDirectClientConf([]string{\"ip:port\"},\"app\",\"token\") // mode2: etcd 服务发现 // conf:=zrpc.NewEtcdClientConf([]string{\"ip:port\"},\"key\",\"app\",\"token\") // client, _ := zrpc.NewClient(conf) // mode3: ip直连mode // client, _ := zrpc.NewClientWithTarget(\"127.0.0.1:8888\") grpc 客户端设置消息大小限制 修改grpc消息大小限制,需要 服务端 和 客户端 都设置 // 需要 grpc 方法里面添加配置项:grpc.MaxCallRecvMsgSize(bytes int)、grpc.MaxCallSendMsgSize(bytes int) // 示例如下:设置 UserRpc.List 列表消息大小限制为 8MB // l.svcCtx.UserRpc.List(l.ctx, &listReq, grpc.MaxCallRecvMsgSize(1024*1024*8), grpc.MaxCallSendMsgSize(1024*1024*8)) faq会不定期更新大家遇到的问题,也欢迎大家把常见问题通过pr写在这里。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-01 16:09:17 "}} \ No newline at end of file +{"./":{"url":"./","title":"简介","keywords":"","body":" go-zero 缩短从需求到上线的距离 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 使用 go-zero 的好处: 轻松获得支撑千万日活服务的稳定性 内建级联超时控制、限流、自适应熔断、自适应降载等微服务治理能力,无需配置和额外代码 微服务治理中间件可无缝集成到其它现有框架使用 极简的 API 描述,一键生成各端代码 自动校验客户端请求参数合法性 大量微服务治理和并发工具包 1. go-zero 框架背景 18 年初,我们决定从 Java+MongoDB 的单体架构迁移到微服务架构,经过仔细思考和对比,我们决定: 基于 Go 语言 高效的性能 简洁的语法 广泛验证的工程效率 极致的部署体验 极低的服务端资源成本 自研微服务框架 有过很多微服务框架自研经验 需要有更快速的问题定位能力 更便捷的增加新特性 2. go-zero 框架设计思考 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 我们经历不到半年时间,彻底完成了从 Java+MongoDB 到 Golang+MySQL 为主的微服务体系迁移,并于 18 年 8 月底完全上线,稳定保障了业务后续迅速增长,确保了整个服务的高可用。 3. go-zero 项目实现和特点 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: 觉得不错的话,别忘 star 👏 4. Installation 在项目目录下通过如下命令安装: GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero 5. Quick Start 完整示例请查看 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 安装 goctl 工具 goctl 读作 go control,不要读成 go C-T-L。goctl 的意思是不要被代码控制,而是要去控制它。其中的 go 不是指 golang。在设计 goctl 之初,我就希望通过 她 来解放我们的双手👈 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 如果使用 go1.16 版本, 可以使用 go install 命令安装 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 确保 goctl 可执行 快速生成 api 服务 goctl api new greet cd greet go mod init go mod tidy go run greet.go -f etc/greet-api.yaml 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: curl -i http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 22 Oct 2020 14:03:18 GMT Content-Length: 14 {\"message\":\"\"} 编写业务代码: api 文件定义了服务对外暴露的路由,可参考 api 规范 可以在 servicecontext.go 里面传递依赖给 logic,比如 mysql, redis 等 在 api 定义的 get/post/put/delete 等请求对应的 logic 里增加业务处理逻辑 可以根据 api 文件生成前端需要的 Java, TypeScript, Dart, JavaScript 代码 goctl api java -api greet.api -dir greet goctl api dart -api greet.api -dir greet ... 6. Benchmark 测试代码见这里 7. 文档 API 文档 goctl 使用帮助 awesome 系列(更多文章见『微服务实践』公众号) 快速构建高并发微服务 快速构建高并发微服务 - 多 RPC 版 精选 goctl 插件 插件 用途 goctl-swagger 一键生成 api 的 swagger 文档 goctl-android 生成 java (android) 端 http client 请求代码 goctl-go-compact 合并 api 里同一个 group 里的 handler 到一个 go 文件 8. 微信公众号 go-zero 相关文章都会在 微服务实践 公众号整理呈现,欢迎扫码关注,也可以通过公众号私信我 👏 9. 微信交流群 如果文档中未能覆盖的任何疑问,欢迎您在群里提出,我们会尽快答复。 您可以在群内提出使用中需要改进的地方,我们会考虑合理性并尽快修改。 如果您发现 bug 请及时提 issue,我们会尽快确认并修改。 为了防止广告用户、识别技术同行,请 star 后加我时注明 github 当前 star 数,我再拉进 go-zero 群,感谢! 加我之前有劳点一下 star,一个小小的 star 是作者们回答海量问题的动力🤝 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"about-us.html":{"url":"about-us.html","title":"关于我们","keywords":"","body":"关于我们 go-zero go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键生成 Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript 代码,并可直接运行。 go-zero作者 万俊峰,七牛云技术副总裁,拥有14年研发团队管理经验,16年架构设计经验,20年工程实战经验,负责过多个大型项目的架构设计,曾多次合伙创业(被收购),阿里云MVP,ArchSummit全球架构师峰会明星讲师,GopherChina大会主持人 & 金牌讲师,QCon+ Go语言出品人兼讲师,腾讯云开发者大会讲师。 go-zero社区 我们目前拥有7000多人的社区成员,在这里,你可以和大家讨论任何关于go-zero的技术,问题反馈,获取最新的go-zero信息,以及各位大佬每天分享的技术心得。 go-zero社区群 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"join-us.html":{"url":"join-us.html","title":"加入我们","keywords":"","body":"加入我们 概要 go-zero 是一个基于MIT License 的开源项目,大家在使用中发现bug,有新的特性等,均可以参与到go-zero的贡献中来,我们非常欢迎大家的积极参与,也会最快响应大家提出的各种问题,pr等。 贡献形式 Pull Request Issue 贡献须知 go-zero 的Pull request中的代码需要满足一定规范 命名规范,请阅读命名规范 以英文注释为主 pr时备注好功能特性,描述需要清晰,简洁 增加单元测试覆盖率达80%+ 贡献代码(pr) 进入go-zero 项目,fork一份go-zero 项目到自己的github仓库中。 回到自己的github主页,找到xx/go-zero项目,其中xx为你的用户名,如anqiansong/go-zero 克隆代码到本地 开发代码,push到自己的github仓库 进入自己的github中go-zero项目,点击浮层上的的【Pull requests】进入Compare页面。 base repository选择zeromicro/go-zero base:master,head repository选择xx/go-zero compare:$branch ,$branch为你开发的分支,如图: 点击【Create pull request】即可实现pr申请 确认pr是否提交成功,进入go-zero 的Pull requests 查看,应该有自己提交的记录,名称为你的开发时的分支名称 Issue 在我们的社区中,有很多伙伴会积极的反馈一些go-zero使用过程中遇到的问题,由于社区人数较多,我们虽然会实时的关注社区动态,但大家问题反馈过来都是随机的,当我们团队还在解决某一个伙伴提出的问题时,另外的问题也反馈上来,可能会导致团队会很容易忽略掉,为了能够一一的解决大家的问题,我们强烈建议大家通过issue的方式来反馈问题,包括但不限于bug,期望的新功能特性等,我们在实现某一个新特性时也会在issue中体现,大家在这里也能够在这里获取到go-zero的最新动向,也欢迎大家来积极的参与讨论。 怎么提Issue 点击这里 进入go-zero的Issue页面或者直接访问https://github.com/zeromicro/go-zero/issues 地址 点击右上角的【New issue】新建issue 填写issue标题和内容 点击【Submit new issue】提交issue 参考文档 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"concept-introduction.html":{"url":"concept-introduction.html","title":"概念介绍","keywords":"","body":"概念介绍 go-zero 晓黑板golang开源项目,集各种工程实践于一身的web和rpc框架。 goctl 一个旨在为开发人员提高工程效率、降低出错率的辅助工具。 goctl插件 指以goctl为中心的周边二进制资源,能够满足一些个性化的代码生成需求,如路由合并插件goctl-go-compact插件, 生成swagger文档的goctl-swagger插件,生成php调用端的goctl-php插件等。 intellij/vscode插件 在intellij系列产品上配合goctl开发的插件,其将goctl命令行操作使用UI进行替代。 api文件 api文件是指用于定义和描述api服务的文本文件,其以.api后缀结尾,包含api语法描述内容。 goctl环境 goctl环境是使用goctl前的准备环境,包含 golang环境 protoc protoc-gen-go插件 go module | gopath go-zero-demo go-zero-demo里面包含了文档中所有源码的一个大仓库,后续我们在编写演示demo时,我们均在此项目下创建子项目, 因此我们需要提前创建一个大仓库go-zero-demo,我这里把这个仓库放在home目录下。 $ cd ~ $ mkdir go-zero-demo&&cd go-zero-demo $ go mod init go-zero-demo 参考文档 go-zero Goctl 插件中心 工具中心 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"quick-start.html":{"url":"quick-start.html","title":"快速开发","keywords":"","body":"快速开发 本节主要通过对 api/rpc 等服务快速开始来让大家对使用 go-zero 开发的工程有一个宏观概念,更加详细的介绍我们将在后续一一展开。如果您已经参考 准备工作 做好环境及工具的准备,请跟随以下小节开始体验: 单体服务 微服务 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"monolithic-service.html":{"url":"monolithic-service.html","title":"单体服务","keywords":"","body":"单体服务 前言 由于go-zero集成了web/rpc于一体,社区有部分小伙伴会问我,go-zero的定位是否是一款微服务框架,答案是不止于此, go-zero虽然集众多功能于一身,但你可以将其中任何一个功能独立出来去单独使用,也可以开发单体服务, 不是说每个服务上来就一定要采用微服务的架构的设计,这点大家可以看看作者(kevin)的第四期开源说 ,其中对此有详细的讲解。 创建greet服务 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done. 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 查看一下greet服务的目录结构 $ tree greet greet ├── etc │ └── greet-api.yaml ├── greet.api ├── greet.go └── internal ├── config │ └── config.go ├── handler │ ├── greethandler.go │ └── routes.go ├── logic │ └── greetlogic.go ├── svc │ └── servicecontext.go └── types └── types.go 由以上目录结构可以观察到,greet服务虽小,但\"五脏俱全\"。接下来我们就可以在greetlogic.go中编写业务代码了。 编写逻辑 $ vim greet/internal/logic/greetlogic.go func (l *GreetLogic) Greet(req *types.Request) (*types.Response, error) { return &types.Response{ Message: \"Hello go-zero\", }, nil } 启动并访问服务 启动服务 $ cd greet $ go run greet.go -f etc/greet-api.yaml 输出如下,服务启动并侦听在8888端口: Starting server at 0.0.0.0:8888... 访问服务 $ curl -i -X GET http://localhost:8888/from/you 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 04:31:25 GMT Content-Length: 27 {\"message\":\"Hello go-zero\"} 源码 greet源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"micro-service.html":{"url":"micro-service.html","title":"微服务","keywords":"","body":"微服务 在上一篇我们已经演示了怎样快速创建一个单体服务,接下来我们来演示一下如何快速创建微服务, 在本小节中,api部分其实和单体服务的创建逻辑是一样的,只是在单体服务中没有服务间的通讯而已, 且微服务中api服务会多一些rpc调用的配置。 前言 本小节将以一个订单服务调用用户服务来简单演示一下,演示代码仅传递思路,其中有些环节不会一一列举。 情景提要 假设我们在开发一个商城项目,而开发者小明负责用户模块(user)和订单模块(order)的开发,我们姑且将这两个模块拆分成两个微服务① [注意] ①:微服务的拆分也是一门学问,这里我们就不讨论怎么去拆分微服务的细节了。 演示功能目标 订单服务(order)提供一个查询接口 用户服务(user)提供一个方法供订单服务获取用户信息 服务设计分析 根据情景提要我们可以得知,订单是直接面向用户,通过http协议访问数据,而订单内部需要获取用户的一些基础数据,既然我们的服务是采用微服务的架构设计, 那么两个服务(user, order)就必须要进行数据交换,服务间的数据交换即服务间的通讯,到了这里,采用合理的通讯协议也是一个开发人员需要 考虑的事情,可以通过http,rpc等方式来进行通讯,这里我们选择rpc来实现服务间的通讯,相信这里我已经对\"rpc服务存在有什么作用?\"已经作了一个比较好的场景描述。 当然,一个服务开发前远不止这点设计分析,我们这里就不详细描述了。从上文得知,我们需要一个 user rpc order api 两个服务来初步实现这个小demo。 创建mall工程 如果你跑了单体的示例,里面也叫 go-zero-demo,你可能需要换一个父目录。 $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo 说明:如无 cd 改变目录的操作,所有操作均在 go-zero-demo 目录执行 创建user rpc服务 创建user rpc服务 $ mkdir -p mall/user/rpc 添加user.proto文件,增加getUser方法 $ vim mall/user/rpc/user.proto 增加如下代码: syntax = \"proto3\"; package user; // protoc-gen-go 版本大于1.4.0, proto文件需要加上go_package,否则无法生成 option go_package = \"./user\"; message IdRequest { string id = 1; } message UserResponse { // 用户id string id = 1; // 用户名称 string name = 2; // 用户性别 string gender = 3; } service User { rpc getUser(IdRequest) returns(UserResponse); } 生成代码 如未安装 protoc,protoc-gen-go,protoc-gen-grpc-go 你可以通过如下指令一键安装: $ goctl env check -i -f 注意: 1、每一个 *.proto文件只允许有一个service error: only one service expected $ cd mall/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. Done. 填充业务逻辑 $ vim internal/logic/getuserlogic.go package logic import ( \"context\" \"go-zero-demo/mall/user/rpc/internal/svc\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetUserLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewGetUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUserLogic { return &GetUserLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *GetUserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) { return &user.UserResponse{ Id: \"1\", Name: \"test\", }, nil } 创建order api服务 创建 order api服务 # 回到 go-zero-demo/mall 目录 $ mkdir -p order/api && cd order/api 添加api文件 $ vim order.api type( OrderReq { Id string `path:\"id\"` } OrderReply { Id string `json:\"id\"` Name string `json:\"name\"` } ) service order { @handler getOrder get /api/order/get/:id (OrderReq) returns (OrderReply) } 生成order服务 $ goctl api go -api order.api -dir . Done. 添加user rpc配置 $ vim internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/zrpc\" \"github.com/zeromicro/go-zero/rest\" ) type Config struct { rest.RestConf UserRpc zrpc.RpcClientConf } 添加yaml配置 $ vim etc/order.yaml Name: order Host: 0.0.0.0 Port: 8888 UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc 完善服务依赖 $ vim internal/svc/servicecontext.go package svc import ( \"go-zero-demo/mall/order/api/internal/config\" \"go-zero-demo/mall/user/rpc/user\" \"github.com/zeromicro/go-zero/zrpc\" ) type ServiceContext struct { Config config.Config UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 添加order演示逻辑 给 getorderlogic 添加业务逻辑 $ vim internal/logic/getorderlogic.go package logic import ( \"context\" \"errors\" \"go-zero-demo/mall/order/api/internal/svc\" \"go-zero-demo/mall/order/api/internal/types\" \"go-zero-demo/mall/user/rpc/types/user\" \"github.com/zeromicro/go-zero/core/logx\" ) type GetOrderLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) GetOrderLogic { return GetOrderLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetOrderLogic) GetOrder(req *types.OrderReq) (*types.OrderReply, error) { user, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdRequest{ Id: \"1\", }) if err != nil { return nil, err } if user.Name != \"test\" { return nil, errors.New(\"用户不存在\") } return &types.OrderReply{ Id: req.Id, Name: \"test order\", }, nil } 启动服务并验证 启动etcd$ etcd 下载依赖# 在 go-zero-demo 目录下 $ go mod tidy 启动user rpc # 在 mall/user/rpc 目录 $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动order api # 在 mall/order/api 目录 $ go run order.go -f etc/order.yaml Starting server at 0.0.0.0:8888... 访问order api $ curl -i -X GET http://localhost:8888/api/order/get/1 HTTP/1.1 200 OK Content-Type: application/json Date: Sun, 07 Feb 2021 03:45:05 GMT Content-Length: 30 {\"id\":\"1\",\"name\":\"test order\"} 注意:在演示中的提及的api语法,rpc生成,goctl,goctl环境等怎么使用和安装,快速入门中不作详细概述,我们后续都会有详细的文档进行描述,你也可以点击下文的【猜你想看】快速跳转的对应文档查看。 源码 mall源码 猜你想看 goctl使用说明 api目录结构介绍 api语法 api配置文件介绍 api中间件使用 rpc目录 rpc配置 rpc调用方说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"framework-design.html":{"url":"framework-design.html","title":"框架设计","keywords":"","body":"框架设计 本节将从 go-zero 的设计理念,go-zero 服务的最佳实践目录来说明 go-zero 框架的设计,本节将包含以下小节: go-zero设计理念 go-zero特点 api语法介绍 api目录结构 rpc目录结构 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-design.html":{"url":"go-zero-design.html","title":"go-zero设计理念","keywords":"","body":"go-zero设计理念 对于微服务框架的设计,我们期望保障微服务稳定性的同时,也要特别注重研发效率。所以设计之初,我们就有如下一些准则: 保持简单,第一原则 弹性设计,面向故障编程 工具大于约定和文档 高可用 高并发 易扩展 对业务开发友好,封装复杂度 约束做一件事只有一种方式 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-features.html":{"url":"go-zero-features.html","title":"go-zero特点","keywords":"","body":"go-zero特性 go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有如下主要特点: 强大的工具支持,尽可能少的代码编写 极简的接口 完全兼容 net/http 支持中间件,方便扩展 高性能 面向故障编程,弹性设计 内建服务发现、负载均衡 内建限流、熔断、降载,且自动触发,自动恢复 API 参数自动校验 超时级联控制 自动缓存控制 链路跟踪、统计报警等 高并发支撑,稳定保障了疫情期间每天的流量洪峰 如下图,我们从多个层面保障了整体服务的高可用: Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-grammar.html":{"url":"api-grammar.html","title":"api语法介绍","keywords":"","body":"api语法介绍 api示例 /** * api语法示例及语法说明 */ // api语法版本 syntax = \"v1\" // import literal import \"foo.api\" // import group import ( \"bar.api\" \"foo/bar.api\" ) info( author: \"songmeizi\" date: \"2020-01-08\" desc: \"api语法示例及语法说明\" ) // type literal type Foo{ Foo int `json:\"foo\"` } // type group type( Bar{ Bar int `json:\"bar\"` } ) // service block @server( jwt: Auth group: foo ) service foo-api{ @doc \"foo\" @handler foo post /foo (Foo) returns (Bar) } api语法结构 syntax语法声明 import语法块 info语法块 type语法块 service语法块 隐藏通道 [!TIP] 在以上语法结构中,各个语法块从语法上来说,按照语法块为单位,可以在.api文件中任意位置声明, 但是为了提高阅读效率,我们建议按照以上顺序进行声明,因为在将来可能会通过严格模式来控制语法块的顺序。 syntax语法声明 syntax是新加入的语法结构,该语法的引入可以解决: 快速针对api版本定位存在问题的语法结构 针对版本做语法解析 防止api语法大版本升级导致前后不能向前兼容 **[!WARNING] 被import的api必须要和main api的syntax版本一致。 语法定义 'syntax'={checkVersion(p)}STRING 语法说明 syntax:固定token,标志一个syntax语法结构的开始 checkVersion:自定义go方法,检测STRING是否为一个合法的版本号,目前检测逻辑为,STRING必须是满足(?m)\"v[1-9][0-9]*\"正则。 STRING:一串英文双引号包裹的字符串,如\"v1\" 一个api语法文件只能有0或者1个syntax语法声明,如果没有syntax,则默认为v1版本 正确语法示例 ✅ eg1:不规范写法 syntax=\"v1\" eg2:规范写法(推荐) syntax = \"v2\" 错误语法示例 ❌ eg1: syntax = \"v0\" eg2: syntax = v1 eg3: syntax = \"V1\" import语法块 随着业务规模增大,api中定义的结构体和服务越来越多,所有的语法描述均为一个api文件,这是多么糟糕的一个问题, 其会大大增加了阅读难度和维护难度,import语法块可以帮助我们解决这个问题,通过拆分api文件, 不同的api文件按照一定规则声明,可以降低阅读难度和维护难度。 **[!WARNING] 这里import不像golang那样包含package声明,仅仅是一个文件路径的引入,最终解析后会把所有的声明都汇聚到一个spec.Spec中。 不能import多个相同路径,否则会解析错误。 语法定义 'import' {checkImportValue(p)}STRING |'import' '(' ({checkImportValue(p)}STRING)+ ')' 语法说明 import:固定token,标志一个import语法的开始 checkImportValue:自定义go方法,检测STRING是否为一个合法的文件路径,目前检测逻辑为,STRING必须是满足(?m)\"(/?[a-zA-Z0-9_#-])+\\.api\"正则。 STRING:一串英文双引号包裹的字符串,如\"foo.api\" 正确语法示例 ✅ eg: import \"foo.api\" import \"foo/bar.api\" import( \"bar.api\" \"foo/bar/foo.api\" ) 错误语法示例 ❌ eg: import foo.api import \"foo.txt\" import ( bar.api bar.api ) info语法块 info语法块是一个包含了多个键值对的语法体,其作用相当于一个api服务的描述,解析器会将其映射到spec.Spec中, 以备用于翻译成其他语言(golang、java等) 时需要携带的meta元素。如果仅仅是对当前api的一个说明,而不考虑其翻译 时传递到其他语言,则使用简单的多行注释或者java风格的文档注释即可,关于注释说明请参考下文的 隐藏通道。 **[!WARNING] 不能使用重复的key,每个api文件只能有0或者1个info语法块 语法定义 'info' '(' (ID {checkKeyValue(p)}VALUE)+ ')' 语法说明 info:固定token,标志一个info语法块的开始 checkKeyValue:自定义go方法,检测VALUE是否为一个合法值。 VALUE:key对应的值,可以为单行的除'\\r','\\n','/'后的任意字符,多行请以\"\"包裹,不过强烈建议所有都以\"\"包裹 正确语法示例 ✅ eg1:不规范写法 info( foo: foo value bar:\"bar value\" desc:\"long long long long long long text\" ) eg2:规范写法(推荐) info( foo: \"foo value\" bar: \"bar value\" desc: \"long long long long long long text\" ) 错误语法示例 ❌ eg1:没有key-value内容 info() eg2:不包含冒号 info( foo value ) eg3:key-value没有换行 info(foo:\"value\") eg4:没有key info( : \"value\" ) eg5:非法的key info( 12: \"value\" ) eg6:移除旧版本多行语法 info( foo: > some text type语法块 在api服务中,我们需要用到一个结构体(类)来作为请求体,响应体的载体,因此我们需要声明一些结构体来完成这件事情, type语法块由golang的type演变而来,当然也保留着一些golang type的特性,沿用golang特性有: 保留了golang内置数据类型bool,int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr ,float32,float64,complex64,complex128,string,byte,rune, 兼容golang struct风格声明 保留golang关键字 **[!WARNING]️ 不支持alias 不支持time.Time数据类型,用int64表示,因为api支持客户端代码生成,并非所有客户端语言都有time.Time对应的类型 结构体名称、字段名称、不能为golang关键字 语法定义 由于其和golang相似,因此不做详细说明,具体语法定义请在 ApiParser.g4 中查看typeSpec定义。 语法说明 参考golang写法 正确语法示例 ✅ eg1:不规范写法 type Foo struct{ Id int `path:\"id\"` // ① Foo int `json:\"foo\"` } type Bar struct{ // 非导出型字段 bar int `form:\"bar\"` } type( // 非导出型结构体 fooBar struct{ FooBar int `json:\"fooBar\"` } ) eg2:规范写法(推荐) type Foo{ Id int `path:\"id\"` Foo int `json:\"foo\"` } type Bar{ Bar int `form:\"bar\"` } type( FooBar{ FooBar int `json:\"fooBar\"` } ) 错误语法示例 ❌ eg type Gender int // 不支持 // 非struct token type Foo structure{ CreateTime time.Time // 不支持time.Time,且没有声明 tag } // golang关键字 var type var{} type Foo{ // golang关键字 interface Foo interface // 没有声明 tag } type Foo{ foo int // map key必须要golang内置数据类型,且没有声明 tag m map[Bar]string } [!NOTE] ① tag定义和golang中json tag语法一样,除了json tag外,go-zero还提供了另外一些tag来实现对字段的描述, 详情见下表。 tag表 绑定参数时,以下四个tag只能选择其中一个 tag key 描述 提供方有效范围 示例 json json序列化tag golang request、response json:\"fooo\" path 路由path,如/foo/:id go-zero request path:\"id\" form 标志请求体是一个form(POST方法时)或者一个query(GET方法时/search?name=keyword) go-zero request form:\"name\" header HTTP header,如 Name: value go-zero request header:\"name\" tag修饰符 常见参数校验描述 tag key 描述 提供方 有效范围 示例 optional 定义当前字段为可选参数 go-zero request json:\"name,optional\" options 定义当前字段的枚举值,多个以竖线|隔开 go-zero request json:\"gender,options=male\" default 定义当前字段默认值 go-zero request json:\"gender,default=male\" range 定义当前字段数值范围 go-zero request json:\"age,range=[0:120]\" [!TIP] tag修饰符需要在tag value后以英文逗号,隔开 service语法块 service语法块用于定义api服务,包含服务名称,服务metadata,中间件声明,路由,handler等。 **[!WARNING]️ main api和被import的api服务名称必须一致,不能出现服务名称歧义。 handler名称不能重复 路由(请求方法+请求path)名称不能重复 请求体必须声明为普通(非指针)struct,响应体做了一些向前兼容处理,详请见下文说明 语法定义 serviceSpec: atServer? serviceApi; atServer: '@server' lp='(' kvLit+ rp=')'; serviceApi: {match(p,\"service\")}serviceToken=ID serviceName lbrace='{' serviceRoute* rbrace='}'; serviceRoute: atDoc? (atServer|atHandler) route; atDoc: '@doc' lp='('? ((kvLit+)|STRING) rp=')'?; atHandler: '@handler' ID; route: {checkHttpMethod(p)}httpMethod=ID path request=body? returnToken=ID? response=replybody?; body: lp='(' (ID)? rp=')'; replybody: lp='(' dataType? rp=')'; // kv kvLit: key=ID {checkKeyValue(p)}value=LINE_VALUE; serviceName: (ID '-'?)+; path: (('/' (ID ('-' ID)*))|('/:' (ID ('-' ID)?)))+; 语法说明 serviceSpec:包含了一个可选语法块atServer和serviceApi语法块,其遵循序列模式(编写service必须要按照顺序,否则会解析出错) atServer: 可选语法块,定义key-value结构的server metadata,'@server' 表示这一个server语法块的开始,其可以用于描述serviceApi或者route语法块,其用于描述不同语法块时有一些特殊关键key 需要值得注意,见 atServer关键key描述说明。 serviceApi:包含了1到多个serviceRoute语法块 serviceRoute:按照序列模式包含了atDoc,handler和route atDoc:可选语法块,一个路由的key-value描述,其在解析后会传递到spec.Spec结构体,如果不关心传递到spec.Spec, 推荐用单行注释替代。 handler:是对路由的handler层描述,可以通过atServer指定handler key来指定handler名称, 也可以直接用atHandler语法块来定义handler名称 atHandler:'@handler' 固定token,后接一个遵循正则[_a-zA-Z][a-zA-Z_-]*)的值,用于声明一个handler名称 route:路由,有httpMethod、path、可选request、可选response组成,httpMethod是必须是小写。 body:api请求体语法定义,必须要由()包裹的可选的ID值 replyBody:api响应体语法定义,必须由()包裹的struct、array(向前兼容处理,后续可能会废弃,强烈推荐以struct包裹,不要直接用array作为响应体) kvLit: 同info key-value serviceName: 可以有多个'-'join的ID值 path:api请求路径,必须以'/'或者'/:'开头,切不能以'/'结尾,中间可包含ID或者多个以'-'join的ID字符串 atServer关键key描述说明 修饰service时 key描述示例 jwt声明当前service下所有路由需要jwt鉴权,且会自动生成包含jwt逻辑的代码jwt: Auth group声明当前service或者路由文件分组group: login middleware声明当前service需要开启中间件middleware: AuthMiddleware prefix添加路由分组prefix: api 修饰route时 key描述示例 handler声明一个handler- 正确语法示例 ✅ eg1:不规范写法 @server( jwt: Auth group: foo middleware: AuthMiddleware prefix api ) service foo-api{ @doc( summary: foo ) @server( handler: foo ) // 非导出型body post /foo/:id (foo) returns (bar) @doc \"bar\" @handler bar post /bar returns ([]int)// 不推荐数组作为响应体 @handler fooBar post /foo/bar (Foo) returns // 可以省略'returns' } eg2:规范写法(推荐) @server( jwt: Auth group: foo middleware: AuthMiddleware prefix: api ) service foo-api{ @doc \"foo\" @handler foo post /foo/:id (Foo) returns (Bar) } service foo-api{ @handler ping get /ping @doc \"foo\" @handler bar post /bar/:id (Foo) } 错误语法示例 ❌ // 不支持空的server语法块 @server( ) // 不支持空的service语法块 service foo-api{ } service foo-api{ @doc kkkk // 简版doc必须用英文双引号引起来 @handler foo post /foo @handler foo // 重复的handler post /bar @handler fooBar post /bar // 重复的路由 // @handler和@doc顺序错误 @handler someHandler @doc \"some doc\" post /some/path // handler缺失 post /some/path/:id @handler reqTest post /foo/req (*Foo) // 不支持除普通结构体外的其他数据类型作为请求体 @handler replyTest post /foo/reply returns (*Foo) // 不支持除普通结构体、数组(向前兼容,后续考虑废弃)外的其他数据类型作为响应体 } 隐藏通道 隐藏通道目前主要为空白符号、换行符号以及注释,这里我们只说注释,因为空白符号和换行符号我们目前拿来也无用。 单行注释 语法定义 '//' ~[\\r\\n]* 语法说明 由语法定义可知道,单行注释必须要以//开头,内容为不能包含换行符 正确语法示例 ✅ // doc // comment 错误语法示例 ❌ // break line comments java风格文档注释 语法定义 '/*' .*? '*/' 语法说明 由语法定义可知道,单行注释必须要以/*开头,*/结尾的任意字符。 正确语法示例 ✅ /** * java-style doc */ 错误语法示例 ❌ /* * java-style doc */ */ Doc&Comment 如果想获取某一个元素的doc或者comment开发人员需要怎么定义? Doc 我们规定上一个语法块(非隐藏通道内容)的行数line+1到当前语法块第一个元素前的所有注释(单行,或者多行)均为doc, 且保留了//、/*、*/原始标记。 Comment 我们规定当前语法块最后一个元素所在行开始的一个注释块(当行,或者多行)为comment 且保留了//、/*、*/原始标记。 语法块Doc和Comment的支持情况 语法块parent语法块DocComment syntaxLitapi✅✅ kvLitinfoSpec✅✅ importLitimportSpec✅✅ typeLitapi✅❌ typeLittypeBlock✅❌ fieldtypeLit✅✅ key-valueatServer✅✅ atHandlerserviceRoute✅✅ routeserviceRoute✅✅ 以下为对应语法块解析后细带doc和comment的写法 // syntaxLit doc syntax = \"v1\" // syntaxLit commnet info( // kvLit doc author: songmeizi // kvLit comment ) // typeLit doc type Foo {} type( // typeLit doc Bar{} FooBar{ // filed doc Name int // filed comment } ) @server( /** * kvLit doc * 开启jwt鉴权 */ jwt: Auth /**kvLit comment*/ ) service foo-api{ // atHandler doc @handler foo //atHandler comment /* * route doc * post请求 * path为 /foo * 请求体:Foo * 响应体:Foo */ post /foo (Foo) returns (Foo) // route comment } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-dir.html":{"url":"api-dir.html","title":"api目录结构","keywords":"","body":"api目录介绍 . ├── etc │ └── greet-api.yaml // 配置文件 ├── go.mod // mod文件 ├── greet.api // api描述文件 ├── greet.go // main函数入口 └── internal ├── config │ └── config.go // 配置声明type ├── handler // 路由及handler转发 │ ├── greethandler.go │ └── routes.go ├── logic // 业务逻辑 │ └── greetlogic.go ├── middleware // 中间件文件 │ └── greetmiddleware.go ├── svc // logic所依赖的资源池 │ └── servicecontext.go └── types // request、response的struct,根据api自动生成,不建议编辑 └── types.go Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-dir.html":{"url":"rpc-dir.html","title":"rpc目录结构","keywords":"","body":"rpc服务目录 proto 文件 greet.proto syntax = \"proto3\"; package stream; option go_package = \"./greet\"; message StreamReq { string name = 1; } message StreamResp { string greet = 1; } service StreamGreeter { rpc greet(StreamReq) returns (StreamResp); } goctl rpc proto $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=. [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is installed [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is installed [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is installed [goctl-env]: congratulations! your goctl environment is ready! [command]: protoc greet.proto --go_out=. --go-grpc_out=. Done. 生成的目录结构 . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet // [1] │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto ├── internal │ ├── config │ │ └── config.go │ ├── logic │ │ └── greetlogic.go │ ├── server │ │ └── streamgreeterserver.go │ └── svc │ └── servicecontext.go └── streamgreeter └── streamgreeter.go [1] pb.go & _grpc.pb.go 文件所在目录并非固定,该目录有 go_opt & go-grpc_opt 与 proto文件中的 go_package 值共同决定,想要了解grpc代码生成目录逻辑请阅读 Go Generated Code Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"project-dev.html":{"url":"project-dev.html","title":"项目开发","keywords":"","body":"项目开发 在前面的章节我们已经从一些概念、背景、快速入门等维度介绍了一下go-zero,看到这里,相信你对go-zero已经有了一些了解, 从这里开始,我们将会从环境准备到服务部署整个流程开始进行讲解,为了保证大家能够彻底弄懂go-zero的开发流程,那就准备你的耐心来接着往下走吧。 在章节中,将包含以下小节: 准备工作 golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 开发规范 命名规范 路由规范 编码规范 开发流程 配置介绍 api配置 rpc配置 业务开发 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 CI/CD 服务部署 日志收集 链路追踪 服务监控 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare.html":{"url":"prepare.html","title":"准备工作","keywords":"","body":"准备工作 在正式进入实际开发之前,我们需要做一些准备工作,比如:Go环境的安装,grpc代码生成使用的工具安装, 必备工具Goctl的安装,Golang环境配置等,本节将包含以下小节: golang安装 go modudle配置 goctl安装 protoc & protoc-gen-go安装 其他 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"golang-install.html":{"url":"golang-install.html","title":"golang安装","keywords":"","body":"Golang环境安装 前言 开发golang程序,必然少不了对其环境的安装,我们这里选择以1.15.1为例。 官方文档 https://golang.google.cn/doc/install mac OS安装Go 下载并安装Go for Mac 验证安装结果 $ go version go version go1.15.1 darwin/amd64 linux 安装Go 下载Go for Linux 解压压缩包至/usr/local $ tar -C /usr/local -xzf go1.15.8.linux-amd64.tar.gz 添加/usr/local/go/bin到环境变量 $ $HOME/.profile export PATH=$PATH:/usr/local/go/bin $ source $HOME/.profile 验证安装结果 $ go version go version go1.15.1 linux/amd64 Windows安装Go 下载并安装Go for Windows 验证安装结果 $ go version go version go1.15.1 windows/amd64 其他 更多操作系统安装见https://golang.org/dl/ Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gomod-config.html":{"url":"gomod-config.html","title":"go module配置","keywords":"","body":"Go Module设置 Go Module介绍 Modules are how Go manages dependencies.[1] 即Go Module是Golang管理依赖性的方式,像Java中的Maven,Android中的Gradle类似。 MODULE配置 查看GO111MODULE开启情况 $ go env GO111MODULE on 开启GO111MODULE,如果已开启(即执行go env GO111MODULE结果为on)请跳过。 $ go env -w GO111MODULE=\"on\" 设置GOPROXY $ go env -w GOPROXY=https://goproxy.cn 设置GOMODCACHE 查看GOMODCACHE $ go env GOMODCACHE 如果目录不为空或者/dev/null,请跳过。 go env -w GOMODCACHE=$GOPATH/pkg/mod 参考文档 [1] Go Modules Reference Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-install.html":{"url":"goctl-install.html","title":"goctl安装","keywords":"","body":"Goctl安装 前言 Goctl在go-zero项目开发着有着很大的作用,其可以有效的帮助开发者大大提高开发效率,减少代码的出错率,缩短业务开发的工作量,更多的Goctl的介绍请阅读Goctl介绍, 在这里我们强烈推荐大家安装,因为后续演示例子中我们大部分都会以goctl进行演示。 安装(mac&linux) download&install # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 环境变量检测 go get下载编译后的二进制文件位于$GOPATH/bin目录下,要确保$GOPATH/bin已经添加到环境变量。 $ sudo vim /etc/paths 在最后一行添加如下内容 $GOPATH/bin [!TIP] $GOPATH为你本机上的文件地址 安装结果验证 $ goctl -v goctl version 1.1.4 darwin/amd64 [!TIP] windows用户添加环境变量请自行google Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"protoc-install.html":{"url":"protoc-install.html","title":"protoc & protoc-gen-go安装","keywords":"","body":"protoc & protoc-gen-go安装 前言 protoc是一款用C++编写的工具,其可以将proto文件翻译为指定语言的代码。在go-zero的微服务中,我们采用grpc进行服务间的通信,而grpc的编写就需要用到protoc和翻译成go语言rpc stub代码的插件protoc-gen-go。 mac OS方式一:goctl一键安装 $ goctl env check -i -f --verbose [goctl-env]: preparing to check env [goctl-env]: looking up \"protoc\" [goctl-env]: \"protoc\" is not found in PATH [goctl-env]: preparing to install \"protoc\" \"protoc\" installed from cache [goctl-env]: \"protoc\" is already installed in \"/Users/keson/go/bin/protoc\" [goctl-env]: looking up \"protoc-gen-go\" [goctl-env]: \"protoc-gen-go\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go\" \"protoc-gen-go\" installed from cache [goctl-env]: \"protoc-gen-go\" is already installed in \"/Users/keson/go/bin/protoc-gen-go\" [goctl-env]: looking up \"protoc-gen-go-grpc\" [goctl-env]: \"protoc-gen-go-grpc\" is not found in PATH [goctl-env]: preparing to install \"protoc-gen-go-grpc\" \"protoc-gen-go-grpc\" installed from cache [goctl-env]: \"protoc-gen-go-grpc\" is already installed in \"/Users/keson/go/bin/protoc-gen-go-grpc\" [goctl-env]: congratulations! your goctl environment is ready! 方式二: 源文件安装 protoc安装 进入protobuf release 页面,选择适合自己操作系统的压缩包文件 解压protoc-x.x.x-osx-x86_64.zip并进入protoc-x.x.x-osx-x86_64 $ cd protoc-x.x.x-osx-x86_64/bin 将启动的protoc二进制文件移动到被添加到环境变量的任意path下,如$GOPATH/bin,这里不建议直接将其和系统的以下path放在一起。 $ mv protoc $GOPATH/bin [!TIP] $GOPATH为你本机的实际文件夹地址 验证安装结果 $ protoc --version libprotoc x.x.x protoc-gen-go/protoc-gen-go-grpc 安装 下载安装protoc-gen-go $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest [!WARNING] protoc-gen-go安装失败请阅读常见错误处理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"prepare-other.html":{"url":"prepare-other.html","title":"其他","keywords":"","body":"其他 在之前我们已经对Go环境、Go Module配置、Goctl、protoc & protoc-gen-go安装准备就绪,这些是开发人员在开发阶段必须要准备的环境,而接下来的环境你可以选择性的安装, 因为这些环境一般存在于服务器(安装工作运维会替你完成),但是为了后续演示流程能够完整走下去,我建议大家在本地也安装一下,因为我们的演示环境大部分会以本地为主。 以下仅给出了需要的准备工作,不以文档篇幅作详细介绍了。 其他环境 etcd redis mysql Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-specification.html":{"url":"dev-specification.html","title":"开发规范","keywords":"","body":"开发规范 在实际业务开发中,除了要提高业务开发效率,缩短业务开发周期,保证线上业务高性能,高可用的指标外,好的编程习惯也是一个开发人员基本素养之一,在本章节, 我们将介绍一下go-zero中的编码规范,本章节为可选章节,内容仅供交流与参考,本章节将从以下小节进行说明: 命名规范 路由规范 编码规范 开发三原则 Clarity(清晰) 作者引用了Hal Abelson and Gerald Sussman的一句话: Programs must be written for people to read, and only incidentally for machines to execute 程序是什么,程序必须是为了开发人员阅读而编写的,只是偶尔给机器去执行,99%的时间程序代码面向的是开发人员,而只有1%的时间可能是机器在执行,这里比例不是重点,从中我们可以看出,清晰的代码是多么的重要,因为所有程序,不仅是Go语言,都是由开发人员编写,供其他人阅读和维护。 Simplicity(简单) Simplicity is prerequisite for reliability Edsger W. Dijkstra认为:可靠的前提条件就是简单,我们在实际开发中都遇到过,这段代码在写什么,想要完成什么事情,开发人员不理解这段代码,因此也不知道如何去维护,这就带来了复杂性,程序越是复杂就越难维护,越难维护就会是程序变得越来越复杂,因此,遇到程序变复杂时首先应该想到的是——重构,重构会重新设计程序,让程序变得简单。 Productivity(生产力) 在go-zero团队中,一直在强调这个话题,开发人员成产力的多少,并不是你写了多少行代码,完成了多少个模块开发,而是我们需要利用各种有效的途径来利用有限的时间完成开发效率最大化,而Goctl的诞生正是为了提高生产力, 因此这个开发原则我是非常认同的。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"naming-spec.html":{"url":"naming-spec.html","title":"命名规范","keywords":"","body":"命名规范 在任何语言开发中,都有其语言领域的一些命名规范,好的命名可以: 降低代码阅读成本 降低维护难度 降低代码复杂度 规范建议 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。 命名准则 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好。 变量命名应尽量描述其内容,而不是类型 常量命名应尽量描述其值,而不是如何使用这个值 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值 method、interface、type、package推荐使用单词命名 package名称也是命名的一部分,请尽量将其利用起来 使用一致的命名风格 文件命名规范 全部小写 除unit test外避免下划线(_) 文件名称不宜过长 变量命名规范参考 首字母小写 驼峰命名 见名知义,避免拼音替代英文 不建议包含下划线(_) 不建议包含数字 适用范围 局部变量 函数出参、入参 函数、常量命名规范 驼峰式命名 可exported的必须首字母大写 不可exported的必须首字母小写 避免全部大写与下划线(_)组合 [!TIP] 如果是go-zero代码贡献,则必须严格遵循此命名规范 参考文档 Practical Go: Real world advice for writing maintainable Go programs Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"route-naming-spec.html":{"url":"route-naming-spec.html","title":"路由规范","keywords":"","body":"路由规范 推荐脊柱式命名 小写单词、横杠(-)组合 见名知义 /user/get-info /user/get/info /user/password/change/:id Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"coding-spec.html":{"url":"coding-spec.html","title":"编码规范","keywords":"","body":"编码规范 import 单行import不建议用圆括号包裹 按照官方包,NEW LINE,当前工程包,NEW LINE,第三方依赖包顺序引入 import ( \"context\" \"string\" \"greet/user/internal/config\" \"google.golang.org/grpc\" ) 函数返回 对象避免非指针返回 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则 错误处理 有error必须处理,如果不能处理就必须抛出。 避免下划线(_)接收error 函数体编码 建议一个block结束空一行,如if、for等 func main (){ if x==1{ // do something } fmt.println(\"xxx\") } return前空一行 func getUser(id string)(string,error){ .... return \"xx\",nil } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"dev-flow.html":{"url":"dev-flow.html","title":"开发流程","keywords":"","body":"开发流程 这里的开发流程和我们实际业务开发流程不是一个概念,这里的定义局限于go-zero的使用,即代码层面的开发细节。 开发流程 goctl环境准备[1] 数据库设计 业务开发 新建工程 创建服务目录 创建服务类型(api/rpc/rmq/job/script) 编写api、proto文件 代码生成 生成数据库访问层代码model 配置config,yaml变更 资源依赖填充(ServiceContext) 添加中间件 业务代码填充 错误处理 [!TIP] [1] goctl环境 开发工具 Visual Studio Code Goland(推荐) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"config-introduction.html":{"url":"config-introduction.html","title":"配置介绍","keywords":"","body":"配置介绍 在正式使用go-zero之前,让我们先来了解一下go-zero中不同服务类型的配置定义,看看配置中每个字段分别有什么作用,本节将包含以下小节: api配置 rpc配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-config.html":{"url":"api-config.html","title":"api配置","keywords":"","body":"api配置 api配置控制着api服务中的各种功能,包含但不限于服务监听地址,端口,环境配置,日志配置等,下面我们从一个简单的配置来看一下api中常用配置分别有什么作用。 配置说明 通过yaml配置我们会发现,有很多参数我们并没有与config对齐,这是因为config定义中,有很多都是带optional或者default 标签的,对于optional可选项,你可以根据自己需求判断是否需要设置,对于default标签,如果你觉得默认值就已经够了,可以不用设置, 一般default中的值基本不用修改,可以认为是最佳实践值。 Config type Config struct{ rest.RestConf // rest api配置 Auth struct { // jwt鉴权配置 AccessSecret string // jwt密钥 AccessExpire int64 // 有效期,单位:秒 } Mysql struct { // 数据库配置,除mysql外,可能还有mongo等其他数据库 DataSource string // mysql链接地址,满足 $user:$password@tcp($ip:$port)/$db?$queries 格式即可 } CacheRedis cache.CacheConf // redis缓存 UserRpc zrpc.RpcClientConf // rpc client配置 } rest.RestConf api服务基础配置,包含监听地址,监听端口,证书配置,限流,熔断参数,超时参数等控制,对其展开我们可以看到: service.ServiceConf // service配置 Host string `json:\",default=0.0.0.0\"` // http监听ip,默认0.0.0.0 Port int // http监听端口,必填 CertFile string `json:\",optional\"` // https证书文件,可选 KeyFile string `json:\",optional\"` // https私钥文件,可选 Verbose bool `json:\",optional\"` // 是否打印详细http请求日志 MaxConns int `json:\",default=10000\"` // http同时可接受最大请求数(限流数),默认10000 MaxBytes int64 `json:\",default=1048576,range=[0:8388608]\"` // http可接受请求的最大ContentLength,默认1048576,被设置值必须在0到8388608之间 // milliseconds Timeout int64 `json:\",default=3000\"` // 超时时长控制,单位:毫秒,默认3000 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` // cpu降载阈值,默认900,可允许设置范围0到1000 Signature SignatureConf `json:\",optional\"` // 签名配置 service.ServiceConf type ServiceConf struct { Name string // 服务名称 Log logx.LogConf // 日志配置 Mode string `json:\",default=pro,options=dev|test|pre|pro\"` // 服务环境,dev-开发环境,test-测试环境,pre-预发环境,pro-正式环境 MetricsUrl string `json:\",optional\"` // 指标上报接口地址,该地址需要支持post json即可 Prometheus prometheus.Config `json:\",optional\"` // prometheus配置 } logx.LogConf type LogConf struct { ServiceName string `json:\",optional\"` // 服务名称 Mode string `json:\",default=console,options=console|file|volume\"` // 日志模式,console-输出到console,file-输出到当前服务器(容器)文件,,volume-输出docker挂载文件内 Path string `json:\",default=logs\"` // 日志存储路径 Level string `json:\",default=info,options=info|error|severe\"` // 日志级别 Compress bool `json:\",optional\"` // 是否开启gzip压缩 KeepDays int `json:\",optional\"` // 日志保留天数 StackCooldownMillis int `json:\",default=100\"` // 日志write间隔 } prometheus.Config type Config struct { Host string `json:\",optional\"` // prometheus 监听host Port int `json:\",default=9101\"` // prometheus 监听端口 Path string `json:\",default=/metrics\"` // 上报地址 } SignatureConf SignatureConf struct { Strict bool `json:\",default=false\"` // 是否Strict模式,如果是则PrivateKeys必填 Expiry time.Duration `json:\",default=1h\"` // 有效期,默认1小时 PrivateKeys []PrivateKeyConf // 签名密钥相关配置 } PrivateKeyConf PrivateKeyConf struct { Fingerprint string // 指纹配置 KeyFile string // 密钥配置 } cache.CacheConf ClusterConf []NodeConf NodeConf struct { redis.RedisConf Weight int `json:\",default=100\"` // 权重 } redis.RedisConf RedisConf struct { Host string // redis地址 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-config.html":{"url":"rpc-config.html","title":"rpc配置","keywords":"","body":"rpc配置 rpc配置控制着一个rpc服务的各种功能,包含但不限于监听地址,etcd配置,超时,熔断配置等,下面我们以一个常见的rpc服务配置来进行说明。 配置说明 Config struct { zrpc.RpcServerConf CacheRedis cache.CacheConf // redis缓存配置,详情见api配置说明,这里不赘述 Mysql struct { // mysql数据库访问配置,详情见api配置说明,这里不赘述 DataSource string } } zrpc.RpcServerConf RpcServerConf struct { service.ServiceConf // 服务配置,详情见api配置说明,这里不赘述 ListenOn string // rpc监听地址和端口,如:127.0.0.1:8888 Etcd discov.EtcdConf `json:\",optional\"` // etcd相关配置 Auth bool `json:\",optional\"` // 是否开启Auth,如果是则Redis为必填 Redis redis.RedisKeyConf `json:\",optional\"` // Auth验证 StrictControl bool `json:\",optional\"` // 是否Strict模式,如果是则遇到错误是Auth失败,否则可以认为成功 // pending forever is not allowed // never set it to 0, if zero, the underlying will set to 2s automatically Timeout int64 `json:\",default=2000\"` // 超时控制,单位:毫秒 CpuThreshold int64 `json:\",default=900,range=[0:1000]\"` cpu降载阈值,默认900,可允许设置范围0到1000 } discov.EtcdConf type EtcdConf struct { Hosts []string // etcd host数组 Key string // rpc注册key } redis.RedisKeyConf RedisConf struct { Host string // redis 主机 Type string `json:\",default=node,options=node|cluster\"` // redis类型 Pass string `json:\",optional\"` // redis密码 } RedisKeyConf struct { RedisConf Key string `json:\",optional\"` // 验证key } Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-dev.html":{"url":"business-dev.html","title":"业务开发","keywords":"","body":"业务开发 本章节我们用一个简单的示例去演示一下go-zero中的一些基本功能。本节将包含以下小节: 目录拆分 model生成 api文件编写 业务编码 jwt鉴权 中间件使用 rpc服务编写与调用 错误处理 演示工程下载 在正式进入后续文档叙述前,可以先留意一下这里的源码,后续我们会基于这份源码进行功能的递进式演示, 而不是完全从0开始,如果你从快速入门章节过来,这份源码结构对你来说不是问题。 点击这里下载演示工程基础源码 演示工程说明 场景 程序员小明需要借阅一本《西游记》,在没有线上图书管理系统的时候,他每天都要去图书馆前台咨询图书馆管理员, 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,明天再来看看吧。 过了一天,小明又来到图书馆,问: 小明:你好,请问今天《西游记》的图书还有吗? 管理员:没有了,你过两天再来看看吧。 就这样经过多次反复,小明也是徒劳无功,浪费大量时间在来回的路上,于是终于忍受不了落后的图书管理系统, 他决定自己亲手做一个图书查阅系统。 预期实现目标 用户登录 依靠现有学生系统数据进行登录 图书检索 根据图书关键字搜索图书,查询图书剩余数量。 系统分析 服务拆分 user api 提供用户登录协议 rpc 供search服务访问用户数据 search api 提供图书查询协议 [!TIP] 这个微小的图书借阅查询系统虽然小,从实际来讲不太符合业务场景,但是仅上面两个功能,已经满足我们对go-zero api/rpc的场景演示了, 后续为了满足更丰富的go-zero功能演示,会在文档中进行业务插入即相关功能描述。这里仅用一个场景进行引入。 注意:user中的sql语句请自行创建到db中去,更多准备工作见准备工作 添加一些预设的用户数据到数据库,便于后面使用,为了篇幅,演示工程不对插入数据这种操作做详细演示。 参考预设数据 INSERT INTO `user` (number,name,password,gender)values ('666','小明','123456','男'); Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-design.html":{"url":"service-design.html","title":"目录拆分","keywords":"","body":"目录拆分 目录拆分是指配合go-zero的最佳实践的目录拆分,这和微服务拆分有着关联,在团队内部最佳实践中, 我们按照业务横向拆分,将一个系统拆分成多个子系统,每个子系统应拥有独立的持久化存储,缓存系统。 如一个商城系统需要有用户系统(user),商品管理系统(product),订单系统(order),购物车系统(cart),结算中心系统(pay),售后系统(afterSale)等组成。 系统结构分析 在上文提到的商城系统中,每个系统在对外(http)提供服务的同时,也会提供数据给其他子系统进行数据访问的接口(rpc),因此每个子系统可以拆分成一个服务,而且对外提供了两种访问该系统的方式api和rpc,因此, 以上系统按照目录结构来拆分有如下结构: . ├── afterSale │ ├── api │ └── rpc ├── cart │ ├── api │ └── rpc ├── order │ ├── api │ └── rpc ├── pay │ ├── api │ └── rpc ├── product │ ├── api │ └── rpc └── user ├── api └── rpc rpc调用链建议 在设计系统时,尽量做到服务之间调用链是单向的,而非循环调用,例如:order服务调用了user服务,而user服务反过来也会调用order的服务, 当其中一个服务启动故障,就会相互影响,进入死循环,你order认为是user服务故障导致的,而user认为是order服务导致的,如果有大量服务存在相互调用链, 则需要考虑服务拆分是否合理。 常见服务类型的目录结构 在上述服务中,仅列举了api/rpc服务,除此之外,一个服务下还可能有其他更多服务类型,如rmq(消息处理系统),cron(定时任务系统),script(脚本)等, 因此一个服务下可能包含以下目录结构: user ├── api // http访问服务,业务需求实现 ├── cronjob // 定时任务,定时数据更新业务 ├── rmq // 消息处理系统:mq和dq,处理一些高并发和延时消息业务 ├── rpc // rpc服务,给其他子系统提供基础数据访问 └── script // 脚本,处理一些临时运营需求,临时数据修复 完整工程目录结构示例 mall // 工程名称 ├── common // 通用库 │ ├── randx │ └── stringx ├── go.mod ├── go.sum └── service // 服务存放目录 ├── afterSale │ ├── api │ └── model │ └── rpc ├── cart │ ├── api │ └── model │ └── rpc ├── order │ ├── api │ └── model │ └── rpc ├── pay │ ├── api │ └── model │ └── rpc ├── product │ ├── api │ └── model │ └── rpc └── user ├── api ├── cronjob ├── model ├── rmq ├── rpc └── script 猜你想看 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"model-gen.html":{"url":"model-gen.html","title":"model生成","keywords":"","body":"model生成 首先,下载好演示工程 后,我们以user的model来进行代码生成演示。 前言 model是服务访问持久化数据层的桥梁,业务的持久化数据常存在于mysql,mongo等数据库中,我们都知道,对于一个数据库的操作莫过于CURD, 而这些工作也会占用一部分时间来进行开发,我曾经在编写一个业务时写了40个model文件,根据不同业务需求的复杂性,平均每个model文件差不多需要 10分钟,对于40个文件来说,400分钟的工作时间,差不多一天的工作量,而goctl工具可以在10秒钟来完成这400分钟的工作。 准备工作 进入演示工程book,找到user/model下的user.sql文件,将其在你自己的数据库中执行建表。 代码生成(带缓存) 方式一(ddl) 进入service/user/model目录,执行命令 $ cd service/user/model $ goctl model mysql ddl -src user.sql -dir . -c Done. 方式二(datasource) $ goctl model mysql datasource -url=\"$datasource\" -table=\"user\" -c -dir . Done. [!TIP] $datasource为数据库连接地址 方式三(intellij 插件) 在Goland中,右键user.sql,依次进入并点击New->Go Zero->Model Code即可生成,或者打开user.sql文件, 进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Mode Code即可 [!TIP] intellij插件生成需要安装goctl插件,详情见intellij插件 验证生成的model文件 查看tree $ tree . ├── user.sql ├── usermodel.go ├── usermodel_gen.go └── vars.go 更多 对于持久化数据,如果需要更灵活的数据库能力,包括事务能力,可以参考 Mysql 如果需要分布式事务的能力,可以参考 分布式事务支持 猜你想看 model命令及其原理 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"api-coding.html":{"url":"api-coding.html","title":"api文件编写","keywords":"","body":"api文件编写 编写user.api文件 $ vim service/user/api/user.api type ( LoginReq { Username string `json:\"username\"` Password string `json:\"password\"` } LoginReply { Id int64 `json:\"id\"` Name string `json:\"name\"` Gender string `json:\"gender\"` AccessToken string `json:\"accessToken\"` AccessExpire int64 `json:\"accessExpire\"` RefreshAfter int64 `json:\"refreshAfter\"` } ) service user-api { @handler login post /user/login (LoginReq) returns (LoginReply) } 生成api服务 方式一 $ cd book/service/user/api $ goctl api go -api user.api -dir . Done. 方式二 在 user.api 文件右键,依次点击进入 New->Go Zero->Api Code ,进入目标目录选择,即api源码的目标存放目录,默认为user.api所在目录,选择好目录后点击OK即可。 方式三 打开user.api,进入编辑区,使用快捷键Command+N(for mac OS)或者 alt+insert(for windows),选择Api Code,同样进入目录选择弹窗,选择好目录后点击OK即可。 猜你想看 api语法 goctl api命令 api目录结构介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"business-coding.html":{"url":"business-coding.html","title":"业务编码","keywords":"","body":"业务编码 前面一节,我们已经根据初步需求编写了user.api来描述user服务对外提供哪些服务访问,在本节我们接着前面的步伐, 通过业务编码来讲述go-zero怎么在实际业务中使用。 添加Mysql配置 $ vim service/user/api/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/core/stores/cache\" ) type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf } 完善yaml配置 $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 更多配置信息,请参考api配置介绍 完善服务依赖 $ vim service/user/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn:=sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn,c.CacheRedis), } } 填充登录逻辑 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) Login(req types.LoginReq) (*types.LoginReply, error) { if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errors.New(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errors.New(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errors.New(\"用户密码不正确\") } // ---start--- now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } // ---end--- return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil } [!TIP] 上述代码中 [start]-[end]的代码实现见jwt鉴权章节 猜你想看 api语法 goctl api命令 api目录结构介绍 jwt鉴权 api配置介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"jwt.html":{"url":"jwt.html","title":"jwt鉴权","keywords":"","body":"jwt鉴权 概述 JSON Web令牌(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑而独立的方法,用于在各方之间安全地将信息作为JSON对象传输。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。 什么时候应该使用JWT 授权:这是使用JWT的最常见方案。一旦用户登录,每个后续请求将包括JWT,从而允许用户访问该令牌允许的路由,服务和资源。单一登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。 信息交换:JSON Web令牌是在各方之间安全地传输信息的一种好方法。因为可以对JWT进行签名(例如,使用公钥/私钥对),所以您可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此您还可以验证内容是否未被篡改。 为什么要使用JSON Web令牌 由于JSON不如XML冗长,因此在编码时JSON的大小也较小,从而使JWT比SAML更为紧凑。这使得JWT是在HTML和HTTP环境中传递的不错的选择。 在安全方面,只能使用HMAC算法由共享机密对SWT进行对称签名。但是,JWT和SAML令牌可以使用X.509证书形式的公用/专用密钥对进行签名。与签署JSON的简单性相比,使用XML Digital Signature签署XML而不引入模糊的安全漏洞是非常困难的。 JSON解析器在大多数编程语言中都很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象的映射。与SAML断言相比,这使使用JWT更加容易。 关于用法,JWT是在Internet规模上使用的。这突显了在多个平台(尤其是移动平台)上对JSON Web令牌进行客户端处理的简便性。 [!TIP] 以上内容全部来自jwt官网介绍 go-zero中怎么使用jwt jwt鉴权一般在api层使用,我们这次演示工程中分别在user api登录时生成jwt token,在search api查询图书时验证用户jwt token两步来实现。 user api生成jwt token 接着业务编码章节的内容,我们完善上一节遗留的getJwtToken方法,即生成jwt token逻辑 添加配置定义和yaml配置项 $ vim service/user/api/internal/config/config.go type Config struct { rest.RestConf Mysql struct{ DataSource string } CacheRedis cache.CacheConf Auth struct { AccessSecret string AccessExpire int64 } } $ vim service/user/api/etc/user-api.yaml Name: user-api Host: 0.0.0.0 Port: 8888 Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:生成jwt token的密钥,最简单的方式可以使用一个uuid值。 $AccessExpire:jwt token有效期,单位:秒 更多配置信息,请参考api配置介绍 $ vim service/user/api/internal/logic/loginlogic.go func (l *LoginLogic) getJwtToken(secretKey string, iat, seconds, userId int64) (string, error) { claims := make(jwt.MapClaims) claims[\"exp\"] = iat + seconds claims[\"iat\"] = iat claims[\"userId\"] = userId token := jwt.New(jwt.SigningMethodHS256) token.Claims = claims return token.SignedString([]byte(secretKey)) } search api使用jwt token鉴权 编写search.api文件 $ vim service/search/api/search.api type ( SearchReq { // 图书名称 Name string `form:\"name\"` } SearchReply { Name string `json:\"name\"` Count int `json:\"count\"` } ) @server( jwt: Auth ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } service search-api { @handler ping get /search/ping } [!TIP] jwt: Auth:开启jwt鉴权 如果路由需要jwt鉴权,则需要在service上方声明此语法标志,如上文中的/search/do 不需要jwt鉴权的路由就无需声明,如上文中/search/ping 更多语法请阅读api语法介绍 生成代码 前面已经描述过有三种方式去生成代码,这里就不赘述了。 添加yaml配置项 $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 这里修改一下端口,避免和user api端口8888冲突 验证 jwt token 启动user api服务,登录 $ cd service/user/api $ go run user.go -f etc/user-api.yaml Starting server at 0.0.0.0:8888... $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'Content-Type: application/json' \\ -d '{ \"username\":\"666\", \"password\":\"123456\" }' 如果是在Windows的CMD里运行,命令格式如下: curl -i -X POST http://127.0.0.1:8888/user/login -H \"Content-Type: application/json\" -d \"{ \\\"username\\\":\\\"666\\\", \\\"password\\\":\\\"123456\\\" }\" 访问结果: HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:37:54 GMT Content-Length: 251 {\"id\":1,\"name\":\"小明\",\"gender\":\"男\",\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80\",\"accessExpire\":1612867074,\"refreshAfter\":1612823874} 启动search api服务,调用/search/do验证jwt鉴权是否通过 $ go run search.go -f etc/search-api.yaml Starting server at 0.0.0.0:8889... 我们先不传jwt token,看看结果 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' HTTP/1.1 401 Unauthorized Date: Mon, 08 Feb 2021 10:41:57 GMT Content-Length: 0 很明显,jwt鉴权失败了,返回401的statusCode,接下来我们带一下jwt token(即用户登录返回的accessToken) $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content-Type: application/json Date: Mon, 08 Feb 2021 10:44:45 GMT Content-Length: 21 {\"name\":\"\",\"count\":0} [!TIP] 服务启动错误,请查看常见错误处理 至此,jwt从生成到使用就演示完成了,jwt token的鉴权是go-zero内部已经封装了,你只需在api文件中定义服务时简单的声明一下即可。 获取jwt token中携带的信息 go-zero从jwt token解析后会将用户生成token时传入的kv原封不动的放在http.Request的Context中,因此我们可以通过Context就可以拿到你想要的值 $ vim /service/search/api/internal/logic/searchlogic.go 添加一个log来输出从jwt解析出来的userId。 func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { logx.Infof(\"userId: %v\",l.ctx.Value(\"userId\"))// 这里的key和生成jwt token时传入的key一致 return &types.SearchReply{}, nil } 运行结果 {\"@timestamp\":\"2021-02-09T10:29:09.399+08\",\"level\":\"info\",\"content\":\"userId: 1\"} 猜你想看 jwt介绍 api配置介绍 api语法 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"middleware.html":{"url":"middleware.html","title":"中间件使用","keywords":"","body":"中间件使用 在上一节,我们演示了怎么使用jwt鉴权,相信你已经掌握了对jwt的基本使用,本节我们来看一下api服务中间件怎么使用。 中间件分类 在go-zero中,中间件可以分为路由中间件和全局中间件,路由中间件是指某一些特定路由需要实现中间件逻辑,其和jwt类似,没有放在jwt:xxx下的路由不会使用中间件功能, 而全局中间件的服务范围则是整个服务。 中间件使用 这里以search服务为例来演示中间件的使用 路由中间件 重新编写search.api文件,添加middleware声明 $ cd service/search/api $ vim search.api type SearchReq struct {} type SearchReply struct {} @server( jwt: Auth middleware: Example // 路由中间件声明 ) service search-api { @handler search get /search/do (SearchReq) returns (SearchReply) } 重新生成api代码 $ goctl api go -api search.api -dir . etc/search-api.yaml exists, ignored generation internal/config/config.go exists, ignored generation search.go exists, ignored generation internal/svc/servicecontext.go exists, ignored generation internal/handler/searchhandler.go exists, ignored generation internal/handler/pinghandler.go exists, ignored generation internal/logic/searchlogic.go exists, ignored generation internal/logic/pinglogic.go exists, ignored generation Done. 生成完后会在internal目录下多一个middleware的目录,这里即中间件文件,后续中间件的实现逻辑也在这里编写。 完善资源依赖ServiceContext $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, } } 编写中间件逻辑 这里仅添加一行日志,内容example middle,如果服务运行输出example middle则代表中间件使用起来了。 $ vim service/search/api/internal/middleware/examplemiddleware.go package middleware import \"net/http\" type ExampleMiddleware struct { } func NewExampleMiddleware() *ExampleMiddleware { return &ExampleMiddleware{} } func (m *ExampleMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // TODO generate middleware implement function, delete after code implementation // Passthrough to next handler if need next(w, r) } } 启动服务验证 {\"@timestamp\":\"2021-02-09T11:32:57.931+08\",\"level\":\"info\",\"content\":\"example middle\"} 全局中间件 通过rest.Server提供的Use方法即可 func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() // 全局中间件 server.Use(func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { logx.Info(\"global middleware\") next(w, r) } }) handler.RegisterHandlers(server, ctx) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } {\"@timestamp\":\"2021-02-09T11:50:15.388+08\",\"level\":\"info\",\"content\":\"global middleware\"} 在中间件里调用其它服务 通过闭包的方式把其它服务传递给中间件,示例如下: // 模拟的其它服务 type AnotherService struct{} func (s *AnotherService) GetToken() string { return stringx.Rand() } // 常规中间件 func middleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", \"static-middleware\") next(w, r) } } // 调用其它服务的中间件 func middlewareWithAnotherService(s *AnotherService) rest.Middleware { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Add(\"X-Middleware\", s.GetToken()) next(w, r) } } } 完整代码参考:https://github.com/zeromicro/zero-examples/tree/main/http/middleware Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"rpc-call.html":{"url":"rpc-call.html","title":"rpc服务编写与调用","keywords":"","body":"rpc编写与调用 在一个大的系统中,多个子系统(服务)间必然存在数据传递,有数据传递就需要通信方式,你可以选择最简单的http进行通信,也可以选择rpc服务进行通信, 在go-zero,我们使用zrpc来进行服务间的通信,zrpc是基于grpc。 场景 在前面我们完善了对用户进行登录,用户查询图书等接口协议,但是用户在查询图书时没有做任何用户校验,如果当前用户是一个不存在的用户则我们不允许其查阅图书信息, 从上文信息我们可以得知,需要user服务提供一个方法来获取用户信息供search服务使用,因此我们就需要创建一个user rpc服务,并提供一个getUser方法。 rpc服务编写 编译proto文件 $ vim service/user/rpc/user.proto syntax = \"proto3\"; package user; option go_package = \"./user\"; message IdReq{ int64 id = 1; } message UserInfoReply{ int64 id = 1; string name = 2; string number = 3; string gender = 4; } service user { rpc getUser(IdReq) returns(UserInfoReply); } 生成rpc服务代码$ cd service/user/rpc $ goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=. [!TIPS] 如果安装的 protoc-gen-go 版大于1.4.0, proto文件建议加上go_package 添加配置及完善yaml配置项 $ vim service/user/rpc/internal/config/config.go type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.CacheConf } $ vim /service/user/rpc/etc/user.yaml Name: user.rpc ListenOn: 127.0.0.1:8080 Etcd: Hosts: - $etcdHost Key: user.rpc Mysql: DataSource: $user:$password@tcp($url)/$db?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: $host Pass: $pass Type: node [!TIP] $user: mysql数据库user $password: mysql数据库密码 $url: mysql数据库连接地址 $db: mysql数据库db名称,即user表所在database $host: redis连接地址 格式:ip:port,如:127.0.0.1:6379 $pass: redis密码 $etcdHost: etcd连接地址,格式:ip:port,如: 127.0.0.1:2379 更多配置信息,请参考rpc配置介绍 添加资源依赖 $ vim service/user/rpc/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config UserModel model.UserModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), } } 添加rpc逻辑 $ service/user/rpc/internal/logic/getuserlogic.go func (l *GetUserLogic) GetUser(in *user.IdReq) (*user.UserInfoReply, error) { one, err := l.svcCtx.UserModel.FindOne(in.Id) if err != nil { return nil, err } return &user.UserInfoReply{ Id: one.Id, Name: one.Name, Number: one.Number, Gender: one.Gender, }, nil } 使用rpc 接下来我们在search服务中调用user rpc 添加UserRpc配置及yaml配置项 $ vim service/search/api/internal/config/config.go type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf } $ vim service/search/api/etc/search-api.yaml Name: search-api Host: 0.0.0.0 Port: 8889 Auth: AccessSecret: $AccessSecret AccessExpire: $AccessExpire UserRpc: Etcd: Hosts: - $etcdHost Key: user.rpc [!TIP] $AccessSecret:这个值必须要和user api中声明的一致。 $AccessExpire: 有效期 $etcdHost: etcd连接地址 etcd中的Key必须要和user rpc服务配置中Key一致 添加依赖 $ vim service/search/api/internal/svc/servicecontext.go type ServiceContext struct { Config config.Config Example rest.Middleware UserRpc user.User } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Example: middleware.NewExampleMiddleware().Handle, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), } } 补充逻辑 $ vim /service/search/api/internal/logic/searchlogic.go func (l *SearchLogic) Search(req types.SearchReq) (*types.SearchReply, error) { userIdNumber := json.Number(fmt.Sprintf(\"%v\", l.ctx.Value(\"userId\"))) logx.Infof(\"userId: %s\", userIdNumber) userId, err := userIdNumber.Int64() if err != nil { return nil, err } // 使用user rpc _, err = l.svcCtx.UserRpc.GetUser(l.ctx, &user.IdReq{ Id: userId, }) if err != nil { return nil, err } return &types.SearchReply{ Name: req.Name, Count: 100, }, nil } 启动并验证服务 启动etcd、redis、mysql 启动user rpc $ cd service/user/rpc $ go run user.go -f etc/user.yaml Starting rpc server at 127.0.0.1:8080... 启动search api $ cd service/search/api $ go run search.go -f etc/search-api.yaml 验证服务 $ curl -i -X GET \\ 'http://127.0.0.1:8889/search/do?name=%E8%A5%BF%E6%B8%B8%E8%AE%B0' \\ -H 'authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTI4NjcwNzQsImlhdCI6MTYxMjc4MDY3NCwidXNlcklkIjoxfQ.JKa83g9BlEW84IiCXFGwP2aSd0xF3tMnxrOzVebbt80' HTTP/1.1 200 OK Content -Type: application/json Date: Tue, 09 Feb 2021 06:05:52 GMT Content-Length: 32 {\"name\":\"西游记\",\"count\":100} 猜你想看 rpc配置 rpc服务目录 goctl rpc命令 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error-handle.html":{"url":"error-handle.html","title":"错误处理","keywords":"","body":"错误处理 错误的处理是一个服务必不可缺的环节。在平时的业务开发中,我们可以认为http状态码不为2xx系列的,都可以认为是http请求错误, 并伴随响应的错误信息,但这些错误信息都是以plain text形式返回的。除此之外,我在业务中还会定义一些业务性错误,常用做法都是通过 code、msg 两个字段来进行业务处理结果描述,并且希望能够以json响应体来进行响应。 业务错误响应格式 业务处理正常 { \"code\": 0, \"msg\": \"successful\", \"data\": { .... } } 业务处理异常 { \"code\": 10001, \"msg\": \"参数错误\" } user api之login 在之前,我们在登录逻辑中处理用户名不存在时,直接返回来一个error。我们来登录并传递一个不存在的用户名看看效果。 curl -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 400 Bad Request Content-Type: text/plain; charset=utf-8 X-Content-Type-Options: nosniff Date: Tue, 09 Feb 2021 06:38:42 GMT Content-Length: 19 用户名不存在 接下来我们将其以json格式进行返回 自定义错误 首先在common中添加一个baseerror.go文件,并填入代码 $ cd common $ mkdir errorx&&cd errorx $ vim baseerror.go package errorx const defaultCode = 1001 type CodeError struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } type CodeErrorResponse struct { Code int `json:\"code\"` Msg string `json:\"msg\"` } func NewCodeError(code int, msg string) error { return &CodeError{Code: code, Msg: msg} } func NewDefaultError(msg string) error { return NewCodeError(defaultCode, msg) } func (e *CodeError) Error() string { return e.Msg } func (e *CodeError) Data() *CodeErrorResponse { return &CodeErrorResponse{ Code: e.Code, Msg: e.Msg, } } 将登录逻辑中错误用CodeError自定义错误替换 if len(strings.TrimSpace(req.Username)) == 0 || len(strings.TrimSpace(req.Password)) == 0 { return nil, errorx.NewDefaultError(\"参数错误\") } userInfo, err := l.svcCtx.UserModel.FindOneByNumber(req.Username) switch err { case nil: case model.ErrNotFound: return nil, errorx.NewDefaultError(\"用户名不存在\") default: return nil, err } if userInfo.Password != req.Password { return nil, errorx.NewDefaultError(\"用户密码不正确\") } now := time.Now().Unix() accessExpire := l.svcCtx.Config.Auth.AccessExpire jwtToken, err := l.getJwtToken(l.svcCtx.Config.Auth.AccessSecret, now, l.svcCtx.Config.Auth.AccessExpire, userInfo.Id) if err != nil { return nil, err } return &types.LoginReply{ Id: userInfo.Id, Name: userInfo.Name, Gender: userInfo.Gender, AccessToken: jwtToken, AccessExpire: now + accessExpire, RefreshAfter: now + accessExpire/2, }, nil 开启自定义错误 $ vim service/user/api/user.go func main() { flag.Parse() var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) server := rest.MustNewServer(c.RestConf) defer server.Stop() handler.RegisterHandlers(server, ctx) // 自定义错误 httpx.SetErrorHandler(func(err error) (int, interface{}) { switch e := err.(type) { case *errorx.CodeError: return http.StatusOK, e.Data() default: return http.StatusInternalServerError, nil } }) fmt.Printf(\"Starting server at %s:%d...\\n\", c.Host, c.Port) server.Start() } 重启服务验证 $ curl -i -X POST \\ http://127.0.0.1:8888/user/login \\ -H 'content-type: application/json' \\ -d '{ \"username\":\"1\", \"password\":\"123456\" }' HTTP/1.1 200 OK Content-Type: application/json Date: Tue, 09 Feb 2021 06:47:29 GMT Content-Length: 40 {\"code\":1001,\"msg\":\"用户名不存在\"} Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"ci-cd.html":{"url":"ci-cd.html","title":"CI/CD","keywords":"","body":"CI/CD 在软件工程中,CI/CD或CICD通常指的是持续集成和持续交付或持续部署的组合实践。 ——引自维基百科 CI可以做什么? 现代应用开发的目标是让多位开发人员同时处理同一应用的不同功能。但是,如果企业安排在一天内将所有分支源代码合并在一起(称为“合并日”),最终可能造成工作繁琐、耗时,而且需要手动完成。这是因为当一位独立工作的开发人员对应用进行更改时,有可能会与其他开发人员同时进行的更改发生冲突。如果每个开发人员都自定义自己的本地集成开发环境(IDE),而不是让团队就一个基于云的 IDE 达成一致,那么就会让问题更加雪上加霜。 持续集成(CI)可以帮助开发人员更加频繁地(有时甚至每天)将代码更改合并到共享分支或“主干”中。一旦开发人员对应用所做的更改被合并,系统就会通过自动构建应用并运行不同级别的自动化测试(通常是单元测试和集成测试)来验证这些更改,确保这些更改没有对应用造成破坏。这意味着测试内容涵盖了从类和函数到构成整个应用的不同模块。如果自动化测试发现新代码和现有代码之间存在冲突,CI 可以更加轻松地快速修复这些错误。 ——引自《CI/CD是什么?如何理解持续集成、持续交付和持续部署》 从概念上来看,CI/CD包含部署过程,我们这里将部署(CD)单独放在一节服务部署, 本节就以gitlab来做简单的CI(Run Unit Test)演示。 gitlab CI Gitlab CI/CD是Gitlab内置的软件开发工具,提供 持续集成(CI) 持续交付(CD) 持续部署(CD) 准备工作 gitlab安装 git安装 gitlab runner安装 开启gitlab CI 上传代码 在gitlab新建一个仓库go-zero-demo 将本地代码上传到go-zero-demo仓库 在项目根目录下创建.gitlab-ci.yaml文件,通过此文件可以创建一个pipeline,其会在代码仓库中有内容变更时运行,pipeline由一个或多个按照顺序运行, 每个阶段可以包含一个或者多个并行运行的job。 添加CI内容(仅供参考) stages: - analysis analysis: stage: analysis image: golang script: - go version && go env - go test -short $(go list ./...) | grep -v \"no test\" [!TIP] 以上CI为简单的演示,详细的gitlab CI请参考gitlab官方文档进行更丰富的CI集成。 参考文档 CI/CD 维基百科 CI/CD是什么?如何理解持续集成、持续交付和持续部署 Gitlab CI Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-deployment.html":{"url":"service-deployment.html","title":"服务部署","keywords":"","body":"服务部署 本节通过jenkins来进行简单的服务部署到k8s演示。 准备工作 k8s集群安装 gitlab环境安装 jenkins环境安装 redis&mysql&nginx&etcd安装 goctl安装 [!TIP] goctl确保k8s每个node节点上都有 以上环境安装请自行google,这里不做篇幅介绍。 服务部署 1、gitlab代码仓库相关准备 1.1、添加SSH Key 进入gitlab,点击用户中心,找到Settings,在左侧找到SSH Keystab 1、在jenkins所在机器上查看公钥 $ cat ~/.ssh/id_rsa.pub 2、如果没有,则需要生成,如果存在,请跳转到第3步 $ ssh-keygen -t rsa -b 2048 -C \"email@example.com\" \"email@example.com\" 可以替换为自己的邮箱 完成生成后,重复第一步操作 3、将公钥添加到gitlab中 1.2、上传代码到gitlab仓库 新建工程go-zero-demo并上传代码,这里不做细节描述。 2、jenkins 2.1、添加凭据 查看jenkins所在机器的私钥,与前面gitlab公钥对应 $ cat id_rsa 进入jenkins,依次点击Manage Jenkins-> Manage Credentials 进入全局凭据页面,添加凭据,Username是一个标识,后面添加pipeline你知道这个标识是代表gitlab的凭据就行,Private Key`即上面获取的私钥 2.2、 添加全局变量 进入Manage Jenkins->Configure System,滑动到全局属性条目,添加docker私有仓库相关信息,如图为docker用户名、docker用户密码、docker私有仓库地址 [!TIP] docker_user 修改为你的docker用户名 docker_pass 修改为你的docker用户密码 docker_server 修改为你的docker服务器地址 这里我使用的私有仓库,如果没有云厂商提供的私有仓库使用,可以自行搭建一个私有仓库,这里就不赘述了,大家自行google。 2.3、配置git 进入Manage Jenkins->Global Tool Configureation,找到Git条目,填写jenkins所在机器git可执行文件所在path,如果没有的话,需要在jenkins插件管理中下载Git插件。 2.4、 添加一个Pipeline pipeline用于构建项目,从gitlab拉取代码->生成Dockerfile->部署到k8s均在这个步骤去做,这里是演示环境,为了保证部署流程顺利, 需要将jenkins安装在和k8s集群的其中过一个节点所在机器上,我这里安装在master上的。 获取凭据id 进入凭据页面,找到Username为gitlab的凭据id 进入jenkins首页,点击新建Item,名称为user 查看项目git地址 添加服务类型Choice Parameter,在General中勾选This project is parameterized,点击添加参数选择Choice Parameter,按照图中添加选择的值常量(api、rpc)及接收值的变量(type),后续在Pipeline script中会用到。 配置user,在user配置页面,向下滑动找到Pipeline script,填写脚本内容 pipeline { agent any parameters { gitParameter name: 'branch', type: 'PT_BRANCH', branchFilter: 'origin/(.*)', defaultValue: 'master', selectedValue: 'DEFAULT', sortMode: 'ASCENDING_SMART', description: '选择需要构建的分支' } stages { stage('服务信息') { steps { sh 'echo 分支:$branch' sh 'echo 构建服务类型:${JOB_NAME}-$type' } } stage('check out') { steps { checkout([$class: 'GitSCM', branches: [[name: '$branch']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '${credentialsId}', url: '${gitUrl}']]]) } } stage('获取commit_id') { steps { echo '获取commit_id' git credentialsId: '${credentialsId}', url: '${gitUrl}' script { env.commit_id = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim() } } } stage('goctl版本检测') { steps{ sh '/usr/local/bin/goctl -v' } } stage('Dockerfile Build') { steps{ sh '/usr/local/bin/goctl docker -go service/${JOB_NAME}/${type}/${JOB_NAME}.go' script{ env.image = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}:${commit_id}').trim() } sh 'echo 镜像名称:${image}' sh 'docker build -t ${image} .' } } stage('上传到镜像仓库') { steps{ sh '/root/dockerlogin.sh' sh 'docker tag ${image} ${dockerServer}/${image}' sh 'docker push ${dockerServer}/${image}' } } stage('部署到k8s') { steps{ script{ env.deployYaml = sh(returnStdout: true, script: 'echo ${JOB_NAME}-${type}-deploy.yaml').trim() env.port=sh(returnStdout: true, script: '/root/port.sh ${JOB_NAME}-${type}').trim() } sh 'echo ${port}' sh 'rm -f ${deployYaml}' sh '/usr/local/bin/goctl kube deploy -secret dockersecret -replicas 2 -nodePort 3${port} -requestCpu 200 -requestMem 50 -limitCpu 300 -limitMem 100 -name ${JOB_NAME}-${type} -namespace hey-go-zero -image ${dockerServer}/${image} -o ${deployYaml} -port ${port}' sh '/usr/bin/kubectl apply -f ${deployYaml}' } } stage('Clean') { steps{ sh 'docker rmi -f ${image}' sh 'docker rmi -f ${dockerServer}/${image}' cleanWs notFailBuild: true } } } } [!TIP] ${credentialsId}要替换为你的具体凭据值,即【添加凭据】模块中的一串字符串,${gitUrl}需要替换为你代码的git仓库地址,其他的${xxx}形式的变量无需修改,保持原样即可。 port.sh参考内容如下 case $1 in \"user-api\") echo 1000 ;; \"user-rpc\") echo 1001 ;; \"course-api\") echo 1002 ;; \"course-rpc\") echo 1003 ;; \"selection-api\") echo 1004 esac 其中dockerlogin.sh内容 #!/bin/bash docker login --username=$docker-user --password=$docker-pass $docker-server $docker-user: docker登录用户名 $docker-pass: docker登录用户密码 $docker-server: docker私有地址 查看pipeline 查看k8s服务 猜你想看 goctl安装 k8s介绍 docker介绍 jenkins安装 jenkins pipeline nginx文档介绍 etcd文档说明 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"log-collection.html":{"url":"log-collection.html","title":"日志收集","keywords":"","body":"日志收集 为了保证业务稳定运行,预测服务不健康风险,日志的收集可以帮助我们很好的观察当前服务的健康状况, 在传统业务开发中,机器部署还不是很多时,我们一般都是直接登录服务器进行日志查看、调试,但随着业务的增大,服务的不断拆分, 服务的维护成本也会随之变得越来越复杂,在分布式系统中,服务器机子增多,服务分布在不同的服务器上,当遇到问题时, 我们不能使用传统做法,登录到服务器进行日志排查和调试,这个复杂度可想而知。 [!TIP] 如果是一个简单的单体服务系统或者服务过于小不建议直接使用,否则会适得其反。 准备工作 kafka elasticsearch kibana filebeat、Log-Pilot(k8s) go-stash filebeat配置 $ vim xx/filebeat.yaml filebeat.inputs: - type: log enabled: true # 开启json解析 json.keys_under_root: true json.add_error_key: true # 日志文件路径 paths: - /var/log/order/*.log setup.template.settings: index.number_of_shards: 1 # 定义kafka topic field fields: log_topic: log-collection # 输出到kafka output.kafka: hosts: [\"127.0.0.1:9092\"] topic: '%{[fields.log_topic]}' partition.round_robin: reachable_only: false required_acks: 1 keep_alive: 10s # ================================= Processors ================================= processors: - decode_json_fields: fields: ['@timestamp','level','content','trace','span','duration'] target: \"\" [!TIP] xx为filebeat.yaml所在路径 go-stash配置 新建config.yaml文件 添加配置内容 $ vim config.yaml Clusters: - Input: Kafka: Name: go-stash Log: Mode: file Brokers: - \"127.0.0.1:9092\" Topics: - log-collection Group: stash Conns: 3 Consumers: 10 Processors: 60 MinBytes: 1048576 MaxBytes: 10485760 Offset: first Filters: - Action: drop Conditions: - Key: status Value: \"503\" Type: contains - Key: type Value: \"app\" Type: match Op: and - Action: remove_field Fields: - source - _score - \"@metadata\" - agent - ecs - input - log - fields Output: ElasticSearch: Hosts: - \"http://127.0.0.1:9200\" Index: \"go-stash-{{yyyy.MM.dd}}\" MaxChunkBytes: 5242880 GracePeriod: 10s Compress: false TimeZone: UTC 启动服务(按顺序启动) 启动kafka 启动elasticsearch 启动kibana 启动go-stash 启动filebeat 启动order-api服务及其依赖服务(go-zero-demo工程中的order-api服务) 访问kibana 进入127.0.0.1:5601 [!TIP] 这里仅演示收集服务中通过logx产生的日志,nginx中日志收集同理。 参考文档 kafka elasticsearch kibana filebeat go-stash filebeat配置 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"trace.html":{"url":"trace.html","title":"链路追踪","keywords":"","body":"go-zero链路追踪 序言 微服务架构中,调用链可能很漫长,从 http 到 rpc ,又从 rpc 到 http 。而开发者想了解每个环节的调用情况及性能,最佳方案就是 全链路跟踪。 追踪的方法就是在一个请求开始时生成一个自己的 spanID ,随着整个请求链路传下去。我们则通过这个 spanID 查看整个链路的情况和性能问题。 下面来看看 go-zero 的链路实现。 代码结构 spancontext :保存链路的上下文信息「traceid,spanid,或者是其他想要传递的内容」 span :链路中的一个操作,存储时间和某些信息 propagator : trace 传播下游的操作「抽取,注入」 noop :实现了空的 tracer 实现 概念 SpanContext 在介绍 span 之前,先引入 context 。SpanContext 保存了分布式追踪的上下文信息,包括 Trace id,Span id 以及其它需要传递到下游的内容。OpenTracing 的实现需要将 SpanContext 通过某种协议 进行传递,以将不同进程中的 Span 关联到同一个 Trace 上。对于 HTTP 请求来说,SpanContext 一般是采用 HTTP header 进行传递的。 下面是 go-zero 默认实现的 spanContext type spanContext struct { traceId string // TraceID 表示tracer的全局唯一ID spanId string // SpanId 标示单个trace中某一个span的唯一ID,在trace中唯一 } 同时开发者也可以实现 SpanContext 提供的接口方法,实现自己的上下文信息传递: type SpanContext interface { TraceId() string // get TraceId SpanId() string // get SpanId Visit(fn func(key, val string) bool) // 自定义操作TraceId,SpanId } Span 一个 REST 调用或者数据库操作等,都可以作为一个 span 。 span 是分布式追踪的最小跟踪单位,一个 Trace 由多段 Span 组成。追踪信息包含如下信息: type Span struct { ctx spanContext // 传递的上下文 serviceName string // 服务名 operationName string // 操作 startTime time.Time // 开始时间戳 flag string // 标记开启trace是 server 还是 client children int // 本 span fork出来的 childsnums } 从 span 的定义结构来看:在微服务中, 这就是一个完整的子调用过程,有调用开始 startTime ,有标记自己唯一属性的上下文结构 spanContext 以及 fork 的子节点数。 实例应用 在 go-zero 中http,rpc中已经作为内置中间件集成。我们以 http ,rpc 中,看看 tracing 是怎么使用的: HTTP func TracingHandler(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // **1** carrier, err := trace.Extract(trace.HttpFormat, r.Header) // ErrInvalidCarrier means no trace id was set in http header if err != nil && err != trace.ErrInvalidCarrier { logx.Error(err) } // **2** ctx, span := trace.StartServerSpan(r.Context(), carrier, sysx.Hostname(), r.RequestURI) defer span.Finish() // **5** r = r.WithContext(ctx) next.ServeHTTP(w, r) }) } func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) ( context.Context, tracespec.Trace) { span := newServerSpan(carrier, serviceName, operationName) // **4** return context.WithValue(ctx, tracespec.TracingKey, span), span } func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace { // **3** traceId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(traceIdKey) } return \"\" }, func() string { return stringx.RandId() }) spanId := stringx.TakeWithPriority(func() string { if carrier != nil { return carrier.Get(spanIdKey) } return \"\" }, func() string { return initSpanId }) return &Span{ ctx: spanContext{ traceId: traceId, spanId: spanId, }, serviceName: serviceName, operationName: operationName, startTime: timex.Time(), // 标记为server flag: serverFlag, } } 将 header -> carrier,获取 header 中的traceId等信息 开启一个新的 span,并把「traceId,spanId」封装在context中 从上述的 carrier「也就是header」获取traceId,spanId 看header中是否设置 如果没有设置,则随机生成返回 从 request 中产生新的ctx,并将相应的信息封装在 ctx 中,返回 从上述的 context,拷贝一份到当前的 request 这样就实现了 span 的信息随着 request 传递到下游服务。 RPC 在 rpc 中存在 client, server ,所以从 tracing 上也有 clientTracing, serverTracing 。 serveTracing 的逻辑基本与 http 的一致,来看看 clientTracing 是怎么使用的? func TracingInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // open clientSpan ctx, span := trace.StartClientSpan(ctx, cc.Target(), method) defer span.Finish() var pairs []string span.Visit(func(key, val string) bool { pairs = append(pairs, key, val) return true }) // **3** 将 pair 中的data以map的形式加入 ctx ctx = metadata.AppendToOutgoingContext(ctx, pairs...) return invoker(ctx, method, req, reply, cc, opts...) } func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) { // **1** if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok { // **2** return span.Fork(ctx, serviceName, operationName) } return ctx, emptyNoopSpan } 获取上游带下来的 span 上下文信息 从获取的 span 中创建新的 ctx,span「继承父span的traceId」 将生成 span 的data加入ctx,传递到下一个中间件,流至下游 总结 go-zero 通过拦截请求获取链路traceID,然后在中间件函数入口会分配一个根Span,然后在后续操作中会分裂出子Span,每个span都有自己的具体的标识,Finsh之后就会汇集在链路追踪系统中。开发者可以通过 ELK 工具追踪 traceID ,看到整个调用链。 同时 go-zero 并没有提供整套 trace 链路方案,开发者可以封装 go-zero 已有的 span 结构,做自己的上报系统,接入 jaeger, zipkin 等链路追踪工具。 参考 go-zero trace Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"service-monitor.html":{"url":"service-monitor.html","title":"服务监控","keywords":"","body":"服务监控 在微服务治理中,服务监控也是非常重要的一个环节,监控一个服务是否正常工作,需要从多维度进行,如: mysql指标 mongo指标 redis指标 请求日志 服务指标统计 服务健康检测 ... 监控的工作非常大,本节仅以其中的服务指标监控作为例子进行说明。 基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 $ prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl.html":{"url":"goctl.html","title":"Goctl","keywords":"","body":"Goctl goctl是go-zero微服务框架下的代码生成工具。使用 goctl 可显著提升开发效率,让开发人员将时间重点放在业务开发上,其功能有: api服务生成 rpc服务生成 model代码生成 模板管理 本节将包含以下内容: 自动补全设置 命令大全 api命令 rpc命令 model命令 plugin命令 其他命令 goctl 读音 很多人会把 goctl 读作 go-C-T-L,这种是错误的念法,应参照 go control 读做 ɡō kənˈtrōl。 查看版本信息 $ goctl -v 如果安装了goctl则会输出以下格式的文本信息: goctl version ${version} ${os}/${arch} 例如输出: goctl version 1.1.5 darwin/amd64 版本号说明 version:goctl 版本号 os:当前操作系统名称 arch: 当前系统架构名称 安装 goctl 方式一(go get) # Go 1.15 及之前版本 GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl@latest # Go 1.16 及以后版本 GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/go-zero/tools/goctl@latest 通过此命令可以将goctl工具安装到 $GOPATH/bin 目录下 方式二 (fork and build) 从 go-zero代码仓库 git@github.com:zeromicro/go-zero.git 拉取一份源码,进入 tools/goctl/目录下编译一下 goctl 文件,然后将其添加到环境变量中。 安装完成后执行goctl -v,如果输出版本信息则代表安装成功,例如: $ goctl -v goctl version 1.1.4 darwin/amd64 常见问题 command not found: goctl 请确保goctl已经安装,或者goctl是否已经正确添加到当前shell的环境变量中。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-completion.html":{"url":"goctl-completion.html","title":"自动补全设置","keywords":"","body":"goctl自动补全 goctl 自动补全仅支持 unix-like 操作系统 用法 $ goctl completion -h NAME: goctl completion - generation completion script, it only works for unix-like OS USAGE: goctl completion [command options] [arguments...] OPTIONS: --name value, -n value the filename of auto complete script, default is [goctl_autocomplete] 生成自动补全文件 $ goctl completion generation auto completion success! executes the following script to setting shell: echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc or echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc shell 配置 zsh$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/zsh/goctl_autocomplete >> ~/.zshrc && source ~/.zshrc bash$ echo PROG=goctl source /Users/keson/.goctl/.auto_complete/bash/goctl_autocomplete >> ~/.bashrc && source ~/.bashrc 演示效果 使用 tab 键出现自动补全提示 $ goctl api -- generate api related files bug -- report a bug completion -- generation completion script, it only works for unix-like OS docker -- generate Dockerfile help h -- Shows a list of commands or help for one command kube -- generate kubernetes files migrate -- migrate from tal-tech to zeromicro model -- generate model code rpc -- generate rpc code template -- template operation upgrade -- upgrade goctl to latest version Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-commands.html":{"url":"goctl-commands.html","title":"命令大全","keywords":"","body":"goctl命令大全 goctl bug (报告一个错误) upgrade (将goctl升级到最新版本) env (检查或编辑goctl环境) --write, -w: 编辑goctl环境 check (检测goctl环境和依赖性工具) --force, -f: 默许安装不存在的依赖项 --install, -i: 如果没有找到,就安装依赖工具 migrate (从tal-tech迁移到zeromicro) --verbose, -v: verbose可以实现额外的日志记录 --version: 要迁移的github.com/zeromicro/go-zero的目标版本。 api (生成api相关文件) --branch:远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 -o:输出api文件 new (快速创建api服务) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/blob/master/tools/goctl/config/readme.md] format (格式化api文件) --declare:用于跳过检查已经声明的api类型 --dir: 格式目标目录 --iu: 忽略更新 --stdin:使用stdin输入api文件内容,按 \"ctrl + d \"发送EOF。 validate (验证api文件) --api: 验证目标api文件 doc (生成文档文件) --dir: 目标目录 --o: 输出markdown目录 go (提供的api生成go文件) --api: api文件 --branch: 远程 repo 的分支,它与 --remote 一起工作。 --dir: 目标目录 --home: 模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] java (为api文件中提供的api生成java文件) --api: api文件 --dir: 目标目录 ts (为api文件中提供的api生成ts文件) --api: api文件 --caller: 网络api调用者 --dir: 目标目录 --unwrap: 解除webapi调用器的包装,以便导入 --webapi: web api文件的路径 dart (为api文件中提供的api生成dart文件) --api: api文件 --dir: 目标目录 --hostname: 服务器的主机名 --legacy: 用于flutter v1的传统生成器 kt (为提供的api文件生成kotlin代码) --api: api文件 --dir: 目标目录 --pkg: 定义kotlin文件的包名 plugin (自定义文件生成器) --api: api文件 --dir: 目标目录 --plugin, -p: 插件文件 --style: 文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] docker (生成Docker文件) --branch:远程版本库的分支,它与--remote一起工作。 --go:包含主函数的文件 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --port:要公开的端口,默认为无(默认:0)。 --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --scratch:使用scratch作为基础docker镜像 --tz:容器的时区(默认:亚洲/上海) --version:goctl builder golang镜像的版本。 kube (生成kubernetes文件) deploy (生成部署yaml文件) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --image:部署的docker镜像 --limitCpu:部署的cpu上限(默认为1000)。 --limitMem: 部署的内存上限(默认为1024)。 --maxReplicas: 部署的最大复制数(默认为10)。 --minReplicas: 部署的最小复制量(默认为3)。 --name:部署的名称 --namespace:部署的命名空间 --nodePort: 要公开的部署的nodePort(默认为0)。 --port: 要在pod上监听的部署的端口(默认值:0) --remote:模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote有更高的优先级。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --replicas:要部署的副本数量(默认:3个)。 --requestCpu:要部署的请求cpu(默认为500)。 --requestMem: 要部署的请求内存(默认为512)。 --revisions: 限制修订历史的数量(默认为5)。 --secret: 从注册表中提取镜像的秘密。 --serviceAccount:部署的ServiceAccount。 -o: 输出的yaml文件 rpc (生成rpc代码) new (生成rpc演示服务) --branch: 远程版本库的分支,它与--remote一起工作。 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 --idea:命令执行环境是否来自idea插件。[可选] --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] template (生成proto模板) --branch:远程repo的分支,它与--remote一起工作。 --home:模板的goctl主路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 --out, -o: proto的目标路径 --remote:模板的远程git repo,--home和--remote不能同时设置,如果有的话,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 protoc (生成grpc代码) --branch:远程repo的分支,它与--remote一起工作。 --home: 模板的goctl主路径 --remote: 模板的远程git repo,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高。 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --zrpc_out:zrpc的输出目录 model (生成model代码) mysql (生成mysql模型) ddl (从ddl生成mysql模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --database, --db:数据库的名称 [可选] - --dir, -d: 目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果设置了,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --src, -s:ddl的路径或路径globbing模式 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c: 使用缓存生成代码 [可选] - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t:数据库中的表或表球化模式 --url:数据库的数据源,如 \"root:password@tcp(127.0.0.1:3306)/database\" pg (生成postgresql模型) datasource (从数据源生成模型) - --branch:远程 repo 的分支,它与 --remote 一起工作。 - --cache, -c:生成带有缓存的代码[可选] 。 - --dir, -d:目标目录 - --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 - --idea:用于理念插件[可选] - --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --schema, -s:表的模式,默认为[public] --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --table, -t: 数据库中的表或表球化模式 --url:数据库的数据源,如 \"postgres://root:password@127.0.0.1:5432/database?sslmode=disable\" mongo (生成mongo模型) --branch:远程repo的分支,它与--remote一起工作。 --cache, -c: 使用缓存生成代码 [可选] --dir, -d:目标目录 --home:模板的goctl首页路径,--home和--remote不能同时设置,如果它们同时设置,--remote的优先级更高 --remote:模板的远程git repo,--home和--remote不能同时设置,如果同时设置,--remote的优先级更高 git repo目录必须与https://github.com/zeromicro/go-zero-template 目录结构一致 --style:文件的命名格式,见[https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --type, -t:指定的模型类型名称 template (模板操作) init (初始化所有模板(强制更新)) --home: 模板的goctl主路径 clean (清理所有缓存的模板) --home: 模板的goctl主路径 update (将目标类别的模板更新为最新的) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] --home: 模板的goctl主页路径 revert (将目标模板恢复到最新版本) --category, -c: 模板的类别,枚举[api,rpc,model,docker,kube] 。 --home:模板的goctl主路径 --name, -n: 模板的目标文件名 completion (生成自动补全脚本,它只适用于类unix操作系统) --name, -n:自动完成脚本的文件名,默认为[goctl_autocomplete] Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-api.html":{"url":"goctl-api.html","title":"api命令","keywords":"","body":"api命令 goctl api是goctl中的核心模块之一,其可以通过.api文件一键快速生成一个api服务,如果仅仅是启动一个go-zero的api演示项目, 你甚至都不用编码,就可以完成一个api服务开发及正常运行。在传统的api项目中,我们要创建各级目录,编写结构体, 定义路由,添加logic文件,这一系列操作,如果按照一条协议的业务需求计算,整个编码下来大概需要5~6分钟才能真正进入业务逻辑的编写, 这还不考虑编写过程中可能产生的各种错误,而随着服务的增多,随着协议的增多,这部分准备工作的时间将成正比上升, 而goctl api则可以完全替代你去做这一部分工作,不管你的协议要定多少个,最终来说,只需要花费10秒不到即可完成。 [!TIP] 其中的结构体编写,路由定义用api进行替代,因此总的来说,省去的是你创建文件夹、添加各种文件及资源依赖的过程的时间。 api命令说明 $ goctl api -h NAME: goctl api - generate api related files USAGE: goctl api command [command options] [arguments...] COMMANDS: new fast create api service format format api files validate validate api file doc generate doc files go generate go files for provided api in yaml file java generate java files for provided api in api file ts generate ts files for provided api in api file dart generate dart files for provided api in api file kt generate kotlin code for provided api file plugin custom file generator OPTIONS: -o value the output api file --help, -h show help 从上文中可以看到,根据功能的不同,api包含了很多的自命令和flag,我们这里重点说明一下 go子命令,其功能是生成golang api服务,我们通过goctl api go -h看一下使用帮助: $ goctl api go -h NAME: goctl api go - generate go files for provided api in yaml file USAGE: goctl api go [command options] [arguments...] OPTIONS: --dir value the target dir --api value the api file --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --dir 代码输出目录 --api 指定api源文件 --style 指定生成代码文件的文件名称风格,详情见文件名称命名style说明 使用示例 $ goctl api go -api user.api -dir . -style gozero 猜你想看 api语法 api目录 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-rpc.html":{"url":"goctl-rpc.html","title":"rpc命令","keywords":"","body":"rpc命令 Goctl Rpc是goctl脚手架下的一个rpc服务代码生成模块,支持proto模板生成和rpc服务代码生成,通过此工具生成代码你只需要关注业务逻辑编写而不用去编写一些重复性的代码。这使得我们把精力重心放在业务上,从而加快了开发效率且降低了代码出错率。 特性 简单易用 快速提升开发效率 出错率低 贴近protoc 快速开始 方式一:快速生成greet服务 通过命令 goctl rpc new ${servieName}生成 如生成greet rpc服务: goctl rpc new greet 执行后代码结构如下: . ├── etc │ └── greet.yaml ├── go.mod ├── go.sum ├── greet │ ├── greet.go │ ├── greet.pb.go │ └── greet_grpc.pb.go ├── greet.go ├── greet.proto └── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go [!TIP] 新版本目录详见 rpc目录 方式二:通过指定proto生成rpc服务 生成proto模板 goctl rpc template -o=user.proto syntax = \"proto3\"; package user; option go_package=\"./user\"; message Request { string ping = 1; } message Response { string pong = 1; } service User { rpc Ping(Request) returns(Response); } 生成rpc服务代码 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. 准备工作 安装了go环境 安装了protoc & protoc-gen-go,并且已经设置环境变量 更多问题请见 注意事项 用法 rpc服务生成用法 goctl rpc protoc -h NAME: goctl rpc protoc - generate grpc code USAGE: example: goctl rpc protoc xx.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=. DESCRIPTION: for details, see https://go-zero.dev/cn/goctl-rpc.html OPTIONS: --zrpc_out value the zrpc output directory --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --home value the goctl home path of the template --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 参数说明 --zrpc_out 可选,默认为proto文件所在目录,生成代码的目标目录 --style 可选,输出目录的文件命名风格,详情见https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md --home 可选,指定模板路径 --remote 可选,指定模板远程仓库 --branch 可选,指定模板远程仓库分支,与 --remote 配合使用 你可以理解为 zrpc 代码生成是用 goctl rpc $protoc_command --zrpc_out=${output} 模板,如原来生成 grpc 代码指令为 $ protoc user.proto --go_out=. --go-grpc_out=. 则生成 zrpc 代码指令就为 $ goctl rpc protoc user.proto --go_out=. --go-grpc_out=. --zrpc_out=. [!TIP] --go_out 与 --go-grpc_out 生成的最终目录必须一致 --go_out & --go-grpc_out 和 --zrpc_out 的生成的最终目录必须不为同一目录,否则pb.go和_grpc.pb.go就与main函数同级了,这是不允许的。 --go_out 与 --go-grpc_out 生产的目录会受 --go_opt 和 --grpc-go_opt 和proto源文件中 go_package值的影响,要想理解这里的生成逻辑,建议阅读 官方文文档:Go Generated Code 开发人员需要做什么 关注业务代码编写,将重复性、与业务无关的工作交给goctl,生成好rpc服务代码后,开发人员仅需要修改 服务中的配置文件编写(etc/xx.json、internal/config/config.go) 服务中业务逻辑编写(internal/logic/xxlogic.go) 服务中资源上下文的编写(internal/svc/servicecontext.go) 注意事项 proto暂不支持多文件同时生成 proto不支持外部依赖包引入,message不支持inline 目前main文件、shared文件、handler文件会被强制覆盖,而和开发人员手动需要编写的则不会覆盖生成,这一类在代码头部均有 // Code generated by goctl. DO NOT EDIT! // Source: xxx.proto 的标识,请注意不要在里面写业务性代码;也不要将它写在业务性代码里面。 proto import 对于rpc中的requestType和returnType必须在main proto文件定义,对于proto中的message可以像protoc一样import其他proto文件。 proto示例: 错误import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { string ping = 1; } message Response { string pong = 1; } service Greet { rpc Ping(base.In) returns(base.Out);// request和return 不支持import } 正确import syntax = \"proto3\"; package greet; option go_package = \"./greet\"; import \"base/common.proto\"; message Request { base.In in = 1;// 支持import } message Response { base.Out out = 2;// 支持import } service Greet { rpc Ping(Request) returns(Response); } 猜你想看 rpc目录 rpc配置 rpc调用 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-model.html":{"url":"goctl-model.html","title":"model命令","keywords":"","body":"model命令 goctl model 为go-zero下的工具模块中的组件之一,目前支持识别mysql ddl进行model层代码生成,通过命令行或者idea插件(即将支持)可以有选择地生成带redis cache或者不带redis cache的代码逻辑。 快速开始 通过ddl生成 $ goctl model mysql ddl -src=\"./*.sql\" -dir=\"./sql/model\" -c 执行上述命令后即可快速生成CURD代码。 model ├── usermodel.go ├── usermodel_gen.go └── vars.go 通过datasource生成 $ goctl model mysql datasource -url=\"user:password@tcp(127.0.0.1:3306)/database\" -table=\"*\" -dir=\"./model\" usermodel_gen.go // Code generated by goctl. DO NOT EDIT! package model import ( \"context\" \"database/sql\" \"fmt\" \"strings\" \"time\" \"github.com/zeromicro/go-zero/core/stores/builder\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlc\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" \"github.com/zeromicro/go-zero/core/stringx\" ) var ( userFieldNames = builder.RawFieldNames(&User{}) userRows = strings.Join(userFieldNames, \",\") userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \",\") userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, \"`id`\", \"`create_time`\", \"`update_time`\"), \"=?,\") + \"=?\" cacheUserIdPrefix = \"cache:user:id:\" cacheUserNumberPrefix = \"cache:user:number:\" ) type ( userModel interface { Insert(ctx context.Context, data *User) (sql.Result, error) FindOne(ctx context.Context, id int64) (*User, error) FindOneByNumber(ctx context.Context, number string) (*User, error) Update(ctx context.Context, data *User) error Delete(ctx context.Context, id int64) error } defaultUserModel struct { sqlc.CachedConn table string } User struct { Id int64 `db:\"id\"` Number string `db:\"number\"` // 学号 Name string `db:\"name\"` // 用户名称 Password string `db:\"password\"` // 用户密码 Gender string `db:\"gender\"` // 男|女|未公开 CreateTime time.Time `db:\"create_time\"` UpdateTime time.Time `db:\"update_time\"` } ) func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel { return &defaultUserModel{ CachedConn: sqlc.NewConn(conn, c), table: \"`user`\", } } func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"insert into %s (%s) values (?, ?, ?, ?)\", m.table, userRowsExpectAutoSet) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender) }, userIdKey, userNumberKey) return ret, err } func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) var resp User err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, id) }) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) { userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, number) var resp User err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where `number` = ? limit 1\", userRows, m.table) if err := conn.QueryRowCtx(ctx, &resp, query, number); err != nil { return nil, err } return resp.Id, nil }, m.queryPrimary) switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } func (m *defaultUserModel) Update(ctx context.Context, data *User) error { userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, data.Id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"update %s set %s where `id` = ?\", m.table, userRowsWithPlaceHolder) return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) Delete(ctx context.Context, id int64) error { data, err := m.FindOne(ctx, id) if err != nil { return err } userIdKey := fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, id) userNumberKey := fmt.Sprintf(\"%s%v\", cacheUserNumberPrefix, data.Number) _, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf(\"delete from %s where `id` = ?\", m.table) return conn.ExecCtx(ctx, query, id) }, userIdKey, userNumberKey) return err } func (m *defaultUserModel) formatPrimary(primary interface{}) string { return fmt.Sprintf(\"%s%v\", cacheUserIdPrefix, primary) } func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where `id` = ? limit 1\", userRows, m.table) return conn.QueryRowCtx(ctx, v, query, primary) } func (m *defaultUserModel) tableName() string { return m.table } usermodel.go package model import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) var _ UserModel = (*customUserModel)(nil) type ( // UserModel is an interface to be customized, add more methods here, // and implement the added methods in customUserModel. UserModel interface { userModel } customUserModel struct { *defaultUserModel } ) // NewUserModel returns a model for the database table. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel { return &customUserModel{ defaultUserModel: newUserModel(conn, c), } } 用法 $ goctl model mysql -h NAME: goctl model mysql - generate mysql model\" USAGE: goctl model mysql command [command options] [arguments...] COMMANDS: ddl generate mysql model from ddl\" datasource generate model from datasource\" OPTIONS: --help, -h show help 生成规则 默认规则 我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,对于这两个字段生成insert、update时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。 ddl NAME: goctl model mysql ddl - generate mysql model from ddl USAGE: goctl model mysql ddl [command options] [arguments...] OPTIONS: --src value, -s value the path or path globbing patterns of the ddl --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --cache, -c generate code with cache [optional] --idea for idea plugin [optional] --database value, --db value the name of database [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote datasource $ goctl model mysql datasource -h  13:40:46 羽106ms NAME: goctl model mysql datasource - generate model from datasource USAGE: goctl model mysql datasource [command options] [arguments...] OPTIONS: --url value the data source of database,like \"root:password@tcp(127.0.0.1:3306)/database\" --table value, -t value the table or table globbing patterns in the database --cache, -c generate code with cache [optional] --dir value, -d value the target dir --style value the file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md] --idea for idea plugin [optional] --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure --branch value the branch of the remote repo, it does work with --remote 生成代码仅基本的CURD结构。 缓存 对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。 缓存会缓存哪些信息? 对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。 数据有更新(update)操作会清空缓存吗? 会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。 为什么不按照单索引字段生成updateByXxx和deleteByXxx的代码? 理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。 为什么不支持findPageLimit、findAll这种模式代码生成? 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 类型转换规则 mysql dataType golang dataType golang dataType(if null&&default null) bool int64 sql.NullInt64 boolean int64 sql.NullInt64 tinyint int64 sql.NullInt64 smallint int64 sql.NullInt64 mediumint int64 sql.NullInt64 int int64 sql.NullInt64 integer int64 sql.NullInt64 bigint int64 sql.NullInt64 float float64 sql.NullFloat64 double float64 sql.NullFloat64 decimal float64 sql.NullFloat64 date time.Time sql.NullTime datetime time.Time sql.NullTime timestamp time.Time sql.NullTime time string sql.NullString year time.Time sql.NullInt64 char string sql.NullString varchar string sql.NullString binary string sql.NullString varbinary string sql.NullString tinytext string sql.NullString text string sql.NullString mediumtext string sql.NullString longtext string sql.NullString enum string sql.NullString set string sql.NullString json string sql.NullString Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-plugin.html":{"url":"goctl-plugin.html","title":"plugin命令","keywords":"","body":"plugin命令 goctl支持针对api自定义插件,那我怎么来自定义一个插件了?来看看下面最终怎么使用的一个例子。 $ goctl api plugin -p goctl-android=\"android -package com.tal\" -api user.api -dir . 上面这个命令可以分解成如下几步: goctl 解析api文件 goctl 将解析后的结构 ApiSpec 和参数传递给goctl-android可执行文件 goctl-android 根据 ApiSpec 结构体自定义生成逻辑。 此命令前面部分 goctl api plugin -p 是固定参数,goctl-android=\"android -package com.tal\" 是plugin参数,其中goctl-android是插件二进制文件,android -package com.tal是插件的自定义参数,-api user.api -dir .是goctl通用自定义参数。 怎么编写自定义插件? go-zero框架中包含了一个很简单的自定义插件 demo,代码如下: package main import ( \"fmt\" \"github.com/zeromicro/go-zero/tools/goctl/plugin\" ) func main() { plugin, err := plugin.NewPlugin() if err != nil { panic(err) } if plugin.Api != nil { fmt.Printf(\"api: %+v \\n\", plugin.Api) } fmt.Printf(\"dir: %s \\n\", plugin.Dir) fmt.Println(\"Enjoy anything you want.\") } plugin, err := plugin.NewPlugin() 这行代码作用是解析从goctl传递过来的数据,里面包含如下部分内容: type Plugin struct { Api *spec.ApiSpec Style string Dir string } [!TIP] Api:定义了api文件的结构数据 Style:可选参数,可以用来控制文件命名规范 Dir:工作目录 完整的基于plugin实现的android plugin演示项目 https://github.com/zeromicro/goctl-android 猜你想看 api目录 api语法 api配置 api命令介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goctl-other.html":{"url":"goctl-other.html","title":"其他命令","keywords":"","body":"其他命令 goctl docker goctl kube goctl docker goctl docker 可以极速生成一个 Dockerfile,帮助开发/运维人员加快部署节奏,降低部署复杂度。 准备工作 docker安装 Dockerfile 额外注意点 选择最简单的镜像:比如alpine,整个镜像5M左右 设置镜像时区RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai 多阶段构建 第一阶段构建出可执行文件,确保构建过程独立于宿主机 第二阶段将第一阶段的输出作为输入,构建出最终的极简镜像 Dockerfile编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 在 greet 项目下创建一个 hello 服务 $ goctl api new hello 文件结构如下: greet ├── go.mod ├── go.sum └── service └── hello ├── Dockerfile ├── etc │ └── hello-api.yaml ├── hello.api ├── hello.go └── internal ├── config │ └── config.go ├── handler │ ├── hellohandler.go │ └── routes.go ├── logic │ └── hellologic.go ├── svc │ └── servicecontext.go └── types └── types.go 在 hello 目录下一键生成 Dockerfile$ goctl docker -go hello.go Dockerfile 内容如下: FROM golang:alpine AS builder LABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOOS linux ENV GOPROXY https://goproxy.cn,direct WORKDIR /build/zero ADD go.mod . ADD go.sum . RUN go mod download COPY . . COPY service/hello/etc /app/etc RUN go build -ldflags=\"-s -w\" -o /app/hello service/hello/hello.go FROM alpine RUN apk update --no-cache RUN apk add --no-cache ca-certificates RUN apk add --no-cache tzdata ENV TZ Asia/Shanghai WORKDIR /app COPY --from=builder /app/hello /app/hello COPY --from=builder /app/etc /app/etc CMD [\"./hello\", \"-f\", \"etc/hello-api.yaml\"] 在 hello 目录下 build 镜像 $ docker build -t hello:v1 -f service/hello/Dockerfile . 查看镜像 hello v1 5455f2eaea6b 7 minutes ago 18.1MB 可以看出镜像大小约为18M。 启动服务$ docker run --rm -it -p 8888:8888 hello:v1 测试服务$ curl -i http://localhost:8888/from/you HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 10 Dec 2020 06:03:02 GMT Content-Length: 14 {\"message\":\"\"} goctl docker总结 goctl 工具极大简化了 Dockerfile 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 goctl kube goctl kube提供了快速生成一个 k8s 部署文件的功能,可以加快开发/运维人员的部署进度,减少部署复杂度。 头疼编写 K8S 部署文件? K8S yaml 参数很多,需要边写边查? 保留回滚版本数怎么设? 如何探测启动成功,如何探活? 如何分配和限制资源? 如何设置时区?否则打印日志是 GMT 标准时间 如何暴露服务供其它服务调用? 如何根据 CPU 和内存使用率来配置水平伸缩? 首先,你需要知道有这些知识点,其次要把这些知识点都搞明白也不容易,再次,每次编写依然容易出错! 创建服务镜像 为了演示,这里我们以 redis:6-alpine 镜像为例。 完整 K8S 部署文件编写过程 首先安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 一键生成 K8S 部署文件 $ goctl kube deploy -name redis -namespace adhoc -image redis:6-alpine -o redis.yaml -port 6379 生成的 yaml 文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: adhoc labels: app: redis spec: replicas: 3 revisionHistoryLimit: 5 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:6-alpine lifecycle: preStop: exec: command: [\"sh\",\"-c\",\"sleep 5\"] ports: - containerPort: 6379 readinessProbe: tcpSocket: port: 6379 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: tcpSocket: port: 6379 initialDelaySeconds: 15 periodSeconds: 20 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1024Mi volumeMounts: - name: timezone mountPath: /etc/localtime volumes: - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: Service metadata: name: redis-svc namespace: adhoc spec: ports: - port: 6379 selector: app: redis --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-c namespace: adhoc labels: app: redis-hpa-c spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 80 --- apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: redis-hpa-m namespace: adhoc labels: app: redis-hpa-m spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: redis minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: memory targetAverageUtilization: 80 部署服务,如果 adhoc namespace 不存在的话,请先通过 kubectl create namespace adhoc 创建 $ kubectl apply -f redis.yaml deployment.apps/redis created service/redis-svc created horizontalpodautoscaler.autoscaling/redis-hpa-c created horizontalpodautoscaler.autoscaling/redis-hpa-m created 查看服务允许状态 $ kubectl get all -n adhoc NAME READY STATUS RESTARTS AGE pod/redis-585bc66876-5ph26 1/1 Running 0 6m5s pod/redis-585bc66876-bfqxz 1/1 Running 0 6m5s pod/redis-585bc66876-vvfc9 1/1 Running 0 6m5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/redis-svc ClusterIP 172.24.15.8 6379/TCP 6m5s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/redis 3/3 3 3 6m6s NAME DESIRED CURRENT READY AGE replicaset.apps/redis-585bc66876 3 3 3 6m6s NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/redis-hpa-c Deployment/redis 0%/80% 3 10 3 6m6s horizontalpodautoscaler.autoscaling/redis-hpa-m Deployment/redis 0%/80% 3 10 3 6m6s 测试服务$ kubectl run -i --tty --rm cli --image=redis:6-alpine -n adhoc -- sh /data # redis-cli -h redis-svc redis-svc:6379> set go-zero great OK redis-svc:6379> get go-zero \"great\" goctl kube 总结 goctl 工具极大简化了 K8S yaml 文件的编写,提供了开箱即用的最佳实践,并且支持了模板自定义。 猜你想看 准备工作 api目录 api语法 api配置 api命令介绍 docker介绍 k8s介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-manage.html":{"url":"template-manage.html","title":"模板管理","keywords":"","body":"模板管理 模板操作 自定义模板 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template-cmd.html":{"url":"template-cmd.html","title":"模板操作","keywords":"","body":"模板操作 模板(Template)是数据驱动生成的基础,所有的代码(rest api、rpc、model、docker、kube)生成都会依赖模板, 默认情况下,模板生成器会选择内存中的模板进行生成,而对于有模板修改需求的开发者来讲,则需要将模板进行落盘, 从而进行模板修改,在下次代码生成时会加载指定路径下的模板进行生成。 使用帮助 NAME: goctl template - template operation USAGE: goctl template command [command options] [arguments...] COMMANDS: init initialize the all templates(force update) clean clean the all cache templates update update template of the target category to the latest revert revert the target template to the latest OPTIONS: --help, -h show help 模板初始化 NAME: goctl template init - initialize the all templates(force update) USAGE: goctl template init [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 清除模板 NAME: goctl template clean - clean the all cache templates USAGE: goctl template clean [command options] [arguments...] OPTIONS: --home value the goctl home path of the template 回滚指定分类模板 NAME: goctl template update - update template of the target category to the latest USAGE: goctl template update [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --home value the goctl home path of the template 回滚模板 NAME: goctl template revert - revert the target template to the latest USAGE: goctl template revert [command options] [arguments...] OPTIONS: --category value, -c value the category of template, enum [api,rpc,model,docker,kube] --name value, -n value the target file name of template --home value the goctl home path of the template [!TIP] --home 指定模板存储路径 模板加载 在代码生成时可以通过--home来指定模板所在文件夹,目前已支持指定模板目录的命令有: goctl api go 详情可以通过goctl api go --help查看帮助 goctl docker 详情可以通过goctl docker --help查看帮助 goctl kube 详情可以通过goctl kube --help查看帮助 goctl rpc new 详情可以通过goctl rpc new --help查看帮助 goctl rpc proto 详情可以通过goctl rpc proto --help查看帮助 goctl model mysql ddl 详情可以通过goctl model mysql ddl --help查看帮助 goctl model mysql datasource 详情可以通过goctl model mysql datasource --help查看帮助 goctl model pg datasource 详情可以通过goctl model pg datasource --help查看帮助 goctl model mongo 详情可以通过goctl model mongo --help查看帮助 默认情况(在不指定--home)会从$HOME/.goctl目录下读取。 使用示例 初始化模板到指定$HOME/template目录下$ goctl template init --home $HOME/template Templates are generated in /Users/anqiansong/template, edit on your risk! 使用$HOME/template模板进行greet rpc生成$ goctl rpc new greet --home $HOME/template Done Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"template.html":{"url":"template.html","title":"自定义模板","keywords":"","body":"模板修改 场景 实现统一格式的body响应,格式如下: { \"code\": 0, \"msg\": \"OK\", \"data\": {} // ① } ① 实际响应数据 [!TIP] go-zero生成的代码没有对其进行处理 准备工作 我们提前在module为greet的工程下的response包中写一个Response方法,目录树类似如下: greet ├── response │ └── response.go └── xxx... 代码如下 package response import ( \"net/http\" \"github.com/zeromicro/go-zero/rest/httpx\" ) type Body struct { Code int `json:\"code\"` Msg string `json:\"msg\"` Data interface{} `json:\"data,omitempty\"` } func Response(w http.ResponseWriter, resp interface{}, err error) { var body Body if err != nil { body.Code = -1 body.Msg = err.Error() } else { body.Msg = \"OK\" body.Data = resp } httpx.OkJson(w, body) } 修改handler模板 $ vim ~/.goctl/api/handler.tpl 将模板替换为以下内容 package handler import ( \"net/http\" \"greet/response\"// ① {% raw %} {{.ImportPackages}} {% endraw %} ) {% raw %} func {{.HandlerName}}(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { {{if .HasRequest}}var req types.{{.RequestType}} if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return }{{end}} l := logic.New{{.LogicType}}(r.Context(), ctx) {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}req{{end}}) {{if .HasResp}}response.Response(w, resp, err){{else}}response.Response(w, nil, err){{end}}//② } } {% endraw %} ① 替换为你真实的response包名,仅供参考 ② 自定义模板内容 [!TIP] 1.如果本地没有~/.goctl/api/handler.tpl文件,可以通过模板初始化命令goctl template init进行初始化 修改模板前后对比 修改前 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) // 以下内容将被自定义模板替换 if err != nil { httpx.Error(w, err) } else { httpx.OkJson(w, resp) } } } 修改后 func GreetHandler(ctx *svc.ServiceContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req types.Request if err := httpx.Parse(r, &req); err != nil { httpx.Error(w, err) return } l := logic.NewGreetLogic(r.Context(), ctx) resp, err := l.Greet(req) response.Response(w, resp, err) } } 修改模板前后响应体对比 修改前 { \"message\": \"Hello go-zero!\" } 修改后 { \"code\": 0, \"msg\": \"OK\", \"data\": { \"message\": \"Hello go-zero!\" } } 总结 本文档仅对http相应为例讲述了自定义模板的流程,除此之外,自定义模板的场景还有: model 层添加kmq model 层生成待有效期option的model实例 http自定义相应格式 ... Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"extended-reading.html":{"url":"extended-reading.html","title":"扩展阅读","keywords":"","body":"扩展阅读 扩展阅读是对go-zero 中的最佳实现和组件的介绍, 因此会比较庞大,而此资源将会持续更新,也欢迎大家来进行文档贡献,本节将包含以下目录(按照文档更新时间排序): 快速构建高并发微服务 日志组件介绍 布隆过滤器 executors 流处理组件 fx go-zero mysql使用介绍 redis锁 periodlimit限流 令牌桶限流 时间轮介绍 熔断原理与实现 进程内缓存组件 collection.Cache 高效的关键词替换和敏感词过滤工具 服务自适应降载保护设计 文本序列化和反序列化 并发处理工具 MapReduce 基于prometheus的微服务指标监控 防止缓存击穿之进程内共享调用 DB缓存机制 zrpc 使用介绍 go-zero缓存设计之持久层缓存 go-zero缓存设计之业务层缓存 go-zero分布式定时任务 我是如何用go-zero 实现一个中台系统 流数据处理利器 10月3日线上交流问题汇总 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"logx.html":{"url":"logx.html","title":"日志组件介绍","keywords":"","body":"logx 使用示例 var c logx.LogConf // 从 yaml 文件中 初始化配置 conf.MustLoad(\"config.yaml\", &c) // logx 根据配置初始化 logx.MustSetup(c) logx.Info(\"This is info!\") logx.Infof(\"This is %s!\", \"info\") logx.Error(\"This is error!\") logx.Errorf(\"this is %s!\", \"error\") logx.Close() 初始化 logx 有很多可以配置项,可以参考 logx.LogConf 中的定义。目前可以使用 logx.MustSetUp(c) 进行初始化配置,如果没有进行初始化配置,所有的配置将使用默认配置。 Level logx 支持的打印日志级别有: alert info error severe fatal slow stat 可以使用对应的方法打印出对应级别的日志。 同时为了方便调试,线上使用,可以动态调整日志打印级别,其中可以通过 logx.SetLevel(uint32) 进行级别设置,也可以通过配置初始化进行设置。目前支持的参数为: const ( // 打印所有级别的日志 InfoLevel = iota // 打印 errors, slows, stacks 日志 ErrorLevel // 仅打印 severe 级别日志 SevereLevel ) 日志模式 目前日志打印模式主要分为2种,一种文件输出,一种控制台输出。推荐方式,当采用 k8s,docker 等部署方式的时候,可以将日志输出到控制台,使用日志收集器收集导入至 es 进行日志分析。如果是直接部署方式,可以采用文件输出方式,logx 会自动在指定文件目录创建对应 5 个对应级别的的日志文件保存日志。 . ├── access.log ├── error.log ├── severe.log ├── slow.log └── stat.log 同时会按照自然日进行文件分割,当超过指定配置天数,会对日志文件进行自动删除,打包等操作。 禁用日志 如果不需要日志打印,可以使用 logx.Close() 关闭日志输出。注意,当禁用日志输出,将无法在次打开,具体可以参考 logx.RotateLogger 和 logx.DailyRotateRule 的实现。 关闭日志 因为 logx 采用异步进行日志输出,如果没有正常关闭日志,可能会造成部分日志丢失的情况。必须在程序退出的地方关闭日志输出: logx.Close() 框架中 rest 和 zrpc 等大部分地方已经做好了日志配置和关闭相关操作,用户可以不用关心。 同时注意,当关闭日志输出之后,将无法在次打印日志了。 推荐写法: import \"github.com/zeromicro/go-zero/core/proc\" // grace close log proc.AddShutdownListener(func() { logx.Close() }) Duration 我们打印日志的时候可能需要打印耗时情况,可以使用 logx.WithDuration(time.Duration), 参考如下示例: startTime := timex.Now() // 数据库查询 rows, err := conn.Query(q, args...) duration := timex.Since(startTime) if duration > slowThreshold { logx.WithDuration(duration).Slowf(\"[SQL] query: slowcall - %s\", stmt) } else { logx.WithDuration(duration).Infof(\"sql query: %s\", stmt) } 会输出如下格式 {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"info\",\"duration\":\"3.0ms\",\"content\":\"sql query:...\"} {\"@timestamp\":\"2020-09-12T01:22:55.552+08\",\"level\":\"slow\",\"duration\":\"500ms\",\"content\":\"[SQL] query: slowcall - ...\"} 这样就可以很容易统计出慢 sql 相关信息。 TraceLog tracingEntry 是为了链路追踪日志输出定制的。可以打印 context 中的 traceId 和 spanId 信息,配合我们的 rest 和 zrpc 很容易完成链路日志的相关打印。示例如下 logx.WithContext(context.Context).Info(\"This is info!\") SysLog 应用中可能有部分采用系统 log 进行日志打印,logx 同样封装方法,很容易将 log 相关的日志收集到 logx 中来。 logx.CollectSysLog() 日志配置相关 LogConf 定义日志系统所需的基本配置 完整定义如下: type LogConf struct { ServiceName string `json:\",optional\"` Mode string `json:\",default=console,options=console|file|volume\"` Path string `json:\",default=logs\"` Level string `json:\",default=info,options=info|error|severe\"` Compress bool `json:\",optional\"` KeepDays int `json:\",optional\"` StackCooldownMillis int `json:\",default=100\"` } Mode Mode 定义了日志打印的方式。默认的模式是 console, 打印到控制台上面。 目前支持的模式如下: console 打印到控制台 file 打印到指定路径下的access.log, error.log, stat.log等文件里 volume 为了在k8s内打印到mount进来的存储上,因为多个pod可能会覆盖相同的文件,volume模式自动识别pod并按照pod分开写各自的日志文件 Path Path 定义了文件日志的输出路径,默认值为 logs。 Level Level 定义了日志打印级别,默认值为 info。 目前支持的级别如下: info error severe Compress Compress 定义了日志是否需要压缩,默认值为 false。在 Mode 为 file 模式下面,文件最后会进行打包压缩成 .gz 文件。 KeepDays KeepDays 定义日志最大保留天数,默认值为 0,表示不会删除旧的日志。在 Mode 为 file 模式下面,如果超过了最大保留天数,旧的日志文件将会被删除。 StackCooldownMillis StackCooldownMillis 定义了日志输出间隔,默认为 100 毫秒。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"bloom.html":{"url":"bloom.html","title":"布隆过滤器","keywords":"","body":"bloom go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 布隆过滤器bloom 在做服务器开发的时候,相信大家有听过布隆过滤器,可以判断某元素在不在集合里面,因为存在一定的误判和删除复杂问题,一般的使用场景是:防止缓存击穿(防止恶意攻击)、 垃圾邮箱过滤、cache digests 、模型检测器等、判断是否存在某行数据,用以减少对磁盘访问,提高服务的访问性能。 go-zero 提供的简单的缓存封装 bloom.bloom,简单使用方式如下 // 初始化 redisBitSet store := redis.New(\"redis 地址\", func(r *redis.Redis) { r.Type = redis.NodeType }) // 声明一个bitSet, key=\"test_key\"名且bits是1024位 bitSet := newRedisBitSet(store, \"test_key\", 1024) // 判断第0位bit存不存在 isSetBefore, err := bitSet.check([]uint{0}) // 对第512位设置为1 err = bitSet.set([]uint{512}) // 3600秒后过期 err = bitSet.expire(3600) // 删除该bitSet err = bitSet.del() bloom 简单介绍了最基本的redis bitset 的使用。下面是真正的bloom实现。 对元素hash 定位 // 对元素进行hash 14次(const maps=14),每次都在元素后追加byte(0-13),然后进行hash. // 将locations[0-13] 进行取模,最终返回locations. func (f *BloomFilter) getLocations(data []byte) []uint { locations := make([]uint, maps) for i := uint(0); i 向bloom里面add 元素 // 我们可以发现 add方法使用了getLocations和bitSet的set方法。 // 我们将元素进行hash成长度14的uint切片,然后进行set操作存到redis的bitSet里面。 func (f *BloomFilter) Add(data []byte) error { locations := f.getLocations(data) err := f.bitSet.set(locations) if err != nil { return err } return nil } 检查bloom里面是否有某元素 // 我们可以发现 Exists方法使用了getLocations和bitSet的check方法 // 我们将元素进行hash成长度14的uint切片,然后进行bitSet的check验证,存在返回true,不存在或者check失败返回false func (f *BloomFilter) Exists(data []byte) (bool, error) { locations := f.getLocations(data) isSet, err := f.bitSet.check(locations) if err != nil { return false, err } if !isSet { return false, nil } return true, nil } 本节主要介绍了go-zero框架中的 core.bloom 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"executors.html":{"url":"executors.html","title":"executors","keywords":"","body":"executors 在 go-zero 中,executors 充当任务池,做多任务缓冲,适用于做批量处理的任务。如:clickhouse 大批量 insert,sql batch insert。同时也可以在 go-queue 中看到 executors 【在 queue 里面使用的是 ChunkExecutor ,限定任务提交字节大小】。 所以当你存在以下需求,都可以使用这个组件: 批量提交任务 缓冲一部分任务,惰性提交 延迟任务提交 具体解释之前,先给一个大致的概览图: 接口设计 在 executors 包下,有如下几个 executor : Name Margin value bulkexecutor 达到 maxTasks 【最大任务数】 提交 chunkexecutor 达到 maxChunkSize【最大字节数】提交 periodicalexecutor basic executor delayexecutor 延迟执行传入的 fn() lessexecutor 你会看到除了有特殊功能的 delay,less ,其余 3 个都是 executor + container 的组合设计: func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor { // 选项模式:在 go-zero 中多处出现。在多配置下,比较好的设计思路 // https://halls-of-valhalla.org/beta/articles/functional-options-pattern-in-go,54/ options := newBulkOptions() for _, opt := range opts { opt(&options) } // 1. task container: [execute 真正做执行的函数] [maxTasks 执行临界点] container := &bulkContainer{ execute: execute, maxTasks: options.cachedTasks, } // 2. 可以看出 bulkexecutor 底层依赖 periodicalexecutor executor := &BulkExecutor{ executor: NewPeriodicalExecutor(options.flushInterval, container), container: container, } return executor } 而这个 container是个 interface: TaskContainer interface { // 把 task 加入 container AddTask(task interface{}) bool // 实际上是去执行传入的 execute func() Execute(tasks interface{}) // 达到临界值,移除 container 中全部的 task,通过 channel 传递到 execute func() 执行 RemoveAll() interface{} } 由此可见之间的依赖关系: bulkexecutor:periodicalexecutor + bulkContainer chunkexecutor:periodicalexecutor + chunkContainer [!TIP] 所以你想完成自己的 executor,可以实现 container 的这 3 个接口,再结合 periodicalexecutor 就行 所以回到👆那张图,我们的重点就放在 periodicalexecutor,看看它是怎么设计的? 如何使用 首先看看如何在业务中使用这个组件: 现有一个定时服务,每天固定时间去执行从 mysql 到 clickhouse 的数据同步: type DailyTask struct { ckGroup *clickhousex.Cluster insertExecutor *executors.BulkExecutor mysqlConn sqlx.SqlConn } 初始化 bulkExecutor: func (dts *DailyTask) Init() { // insertIntoCk() 是真正insert执行函数【需要开发者自己编写具体业务逻辑】 dts.insertExecutor = executors.NewBulkExecutor( dts.insertIntoCk, executors.WithBulkInterval(time.Second*3), // 3s会自动刷一次container中task去执行 executors.WithBulkTasks(10240), // container最大task数。一般设为2的幂次 ) } [!TIP] 额外介绍一下:clickhouse 适合大批量的插入,因为 insert 速度很快,大批量 insert 更能充分利用 clickhouse 主体业务逻辑编写: func (dts *DailyTask) insertNewData(ch chan interface{}, sqlFromDb *model.Task) error { for item := range ch { if r, vok := item.(*model.Task); !vok { continue } err := dts.insertExecutor.Add(r) if err != nil { r.Tag = sqlFromDb.Tag r.TagId = sqlFromDb.Id r.InsertId = genInsertId() r.ToRedis = toRedis == constant.INCACHED r.UpdateWay = sqlFromDb.UpdateWay // 1. Add Task err := dts.insertExecutor.Add(r) if err != nil { logx.Error(err) } } } // 2. Flush Task container dts.insertExecutor.Flush() // 3. Wait All Task Finish dts.insertExecutor.Wait() } [!TIP] 可能会疑惑为什么要 Flush(), Wait() ,后面会通过源码解析一下 使用上总体分为 3 步: Add():加入 task Flush():刷新 container 中的 task Wait():等待全部 task 执行完成 源码分析 [!TIP] 此处主要分析 periodicalexecutor,因为其他两个常用的 executor 都依赖它 初始化 func New...(interval time.Duration, container TaskContainer) *PeriodicalExecutor { executor := &PeriodicalExecutor{ commander: make(chan interface{}, 1), interval: interval, container: container, confirmChan: make(chan lang.PlaceholderType), newTicker: func(d time.Duration) timex.Ticker { return timex.NewTicker(interval) }, } ... return executor } commander:传递 tasks 的 channel container:暂存 Add() 的 task confirmChan:阻塞 Add() ,在开始本次的 executeTasks() 会放开阻塞 ticker:定时器,防止 Add() 阻塞时,会有一个定时执行的机会,及时释放暂存的 task Add() 初始化完,在业务逻辑的第一步就是把 task 加入 executor: func (pe *PeriodicalExecutor) Add(task interface{}) { if vals, ok := pe.addAndCheck(task); ok { pe.commander =maxTask 将container中tasks pop, return if pe.container.AddTask(task) { return pe.container.RemoveAll(), true } return nil, false } addAndCheck() 中 AddTask() 就是在控制最大 tasks 数,如果超过就执行 RemoveAll() ,将暂存 container 的 tasks pop,传递给 commander ,后面有 goroutine 循环读取,然后去执行 tasks。 backgroundFlush() 开启一个后台协程,对 container 中的 task,不断刷新: func (pe *PeriodicalExecutor) backgroundFlush() { // 封装 go func(){} threading.GoSafe(func() { ticker := pe.newTicker(pe.interval) defer ticker.Stop() var commanded bool last := timex.Now() for { select { // 从channel拿到 []tasks case vals := pe.interval*idleRound { // 既没到maxTask,Flush() err,并且 last->now 时间过长,会再次触发 Flush() // 只有这置反,才会开启一个新的 backgroundFlush() 后台协程 pe.guarded = false // 再次刷新,防止漏掉 pe.Flush() return } } } }) } 总体两个过程: commander 接收到 RemoveAll() 传递来的 tasks,然后执行,并放开 Add() 的阻塞,得以继续 Add() ticker 到时间了,如果第一步没有执行,则自动 Flush() ,也会去做 task 的执行 Wait() 在 backgroundFlush() ,提到一个函数:enterExecution(): func (pe *PeriodicalExecutor) enterExecution() { pe.wgBarrier.Guard(func() { pe.waitGroup.Add(1) }) } func (pe *PeriodicalExecutor) Wait() { pe.wgBarrier.Guard(func() { pe.waitGroup.Wait() }) } 这样列举就知道为什么之前在最后要带上 dts.insertExecutor.Wait(),当然要等待全部的 goroutine task 完成。 思考 在看源码中,思考了一些其他设计上的思路,大家是否也有类似的问题: 在分析 executors 中,会发现很多地方都有 lock [!TIP] go test 存在竞态,使用加锁来避免这种情况 在分析 confirmChan 时发现,confirmChan 在此次提交才出现,为什么会这么设计? 之前是:wg.Add(1) 是写在 executeTasks() ;现在是:先wg.Add(1),再放开 confirmChan 阻塞 如果 executor func 执行阻塞,Add task 还在进行,因为没有阻塞,可能很快执行到 Executor.Wait(),这时就会出现 wg.Wait() 在 wg.Add() 前执行,这会 panic 具体可以看最新版本的TestPeriodicalExecutor_WaitFast() ,不妨跑在此版本上,就可以重现 总结 剩余还有几个 executors 的分析,就留给大家去看看源码。 总之,整体设计上: 遵循面向接口设计 灵活使用 channel ,waitgroup 等并发工具 执行单元+存储单元的搭配使用 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"fx.html":{"url":"fx.html","title":"流处理组件 fx","keywords":"","body":"数据的流处理利器 流处理(Stream processing)是一种计算机编程范式,其允许给定一个数据序列(流处理数据源),一系列数据操作(函数)被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到Kafka,然后再由Job消费Kafaka写到elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx gozero是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce工具进行处理。 总结 本篇文章介绍了流处理的基本概念和gozero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mysql.html":{"url":"mysql.html","title":"go-zero mysql使用介绍","keywords":"","body":"mysql go-zero 提供更易于操作的 mysql API。 [!TIP] 但是 stores/mysql 定位不是一个 orm 框架,如果你需要通过 sql/scheme -> model/struct 逆向生成 model 层代码,可以使用「goctl model」,这个是极好的功能。 Feature 相比原生,提供对开发者更友好的 API 完成 queryField -> struct 的自动赋值 批量插入「bulkinserter」 自带熔断 API 经过若干个服务的不断考验 提供 partial assignment 特性,不强制 struct 的严格赋值 Connection 下面用一个例子简单说明一下如何创建一个 mysql 连接的 model: // 1. 快速连接一个 mysql // datasource: mysql dsn heraMysql := sqlx.NewMysql(datasource) // 2. 在 servicecontext 中调用,懂model上层的logic层调用 model.NewMysqlModel(heraMysql, tablename), // 3. model层 mysql operation func NewMysqlModel(conn sqlx.SqlConn, table string) *MysqlModel { defer func() { recover() }() // 4. 创建一个批量insert的 [mysql executor] // conn: mysql connection; insertsql: mysql insert sql bulkInserter , err := sqlx.NewBulkInserter(conn, insertsql) if err != nil { logx.Error(\"Init bulkInsert Faild\") panic(\"Init bulkInsert Faild\") return nil } return &MysqlModel{conn: conn, table: table, Bulk: bulkInserter} } CRUD 准备一个 User model var userBuilderQueryRows = strings.Join(builder.FieldNames(&User{}), \",\") type User struct { Avatar string `db:\"avatar\"` // 头像 UserName string `db:\"user_name\"` // 姓名 Sex int `db:\"sex\"` // 1男,2女 MobilePhone string `db:\"mobile_phone\"` // 手机号 } 其中 userBuilderQueryRows : go-zero 中提供 struct -> [field...] 的转化,开发者可以将此当成模版直接使用。 insert // 一个实际的insert model层操作 func (um *UserModel) Insert(user *User) (int64, error) { const insertsql = `insert into `+um.table+` (`+userBuilderQueryRows+`) values(?, ?, ?)` // insert op res, err := um.conn.Exec(insertsql, user.Avatar, user.UserName, user.Sex, user.MobilePhone) if err != nil { logx.Errorf(\"insert User Position Model Model err, err=%v\", err) return -1, err } id, err := res.LastInsertId() if err != nil { logx.Errorf(\"insert User Model to Id parse id err,err=%v\", err) return -1, err } return id, nil } 拼接 insertsql 将 insertsql 以及占位符对应的 struct field 传入 -> con.Exex(insertsql, field...) [!WARNING] conn.Exec(sql, args...) : args... 需对应 sql 中的占位符。不然会出现赋值异常的问题。 go-zero 将涉及 mysql 修改的操作统一抽象为 Exec() 。所以 insert/update/delete 操作本质上是一致的。其余两个操作,开发者按照上述 insert 流程尝试即可。 query 只需要传入 querysql 和 model 结构体,就可以获取到被赋值好的 model 。无需开发者手动赋值。 func (um *UserModel) FindOne(uid int64) (*User, error) { var user User const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where id=? limit 1` err := um.conn.QueryRow(&user, querysql, uid) if err != nil { logx.Errorf(\"userId.findOne error, id=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return &user, nil } 声明 model struct ,拼接 querysql conn.QueryRow(&model, querysql, args...) : args... 与 querysql 中的占位符对应。 [!WARNING] QueryRow() 中第一个参数需要传入 Ptr 「底层需要反射对 struct 进行赋值」 上述是查询一条记录,如果需要查询多条记录时,可以使用 conn.QueryRows() func (um *UserModel) FindOne(sex int) ([]*User, error) { users := make([]*User, 0) const querysql = `select `+userBuilderQueryRows+` from `+um.table+` where sex=?` err := um.conn.QueryRows(&users, querysql, sex) if err != nil { logx.Errorf(\"usersSex.findOne error, sex=%d, err=%s\", uid, err.Error()) if err == sqlx.ErrNotFound { return nil, ErrNotFound } return nil, err } return users, nil } 与 QueryRow() 不同的地方在于: model 需要设置成 Slice ,因为是查询多行,需要对多个 model 赋值。但同时需要注意️:第一个参数需要传入 Ptr querypartial 从使用上,与上述的 QueryRow() 无异「这正体现了 go-zero 高度的抽象设计」。 区别: QueryRow() : len(querysql fields) == len(struct) ,且一一对应 QueryRowPartial() :len(querysql fields) numA:数据库字段数;numB:定义的 struct 属性数。 如果 numA ,但是你恰恰又需要统一多处的查询时「定义了多个 struct 返回不同的用途,恰恰都可以使用相同的 querysql 」,就可以使用 QueryRowPartial() 事务 要在事务中执行一系列操作,一般流程如下: var insertsql = `insert into User(uid, username, mobilephone) values (?, ?, ?)` err := usermodel.conn.Transact(func(session sqlx.Session) error { stmt, err := session.Prepare(insertsql) if err != nil { return err } defer stmt.Close() // 返回任何错误都会回滚事务 if _, err := stmt.Exec(uid, username, mobilephone); err != nil { logx.Errorf(\"insert userinfo stmt exec: %s\", err) return err } // 还可以继续执行 insert/update/delete 相关操作 return nil }) 如同上述例子,开发者只需将 事务 中的操作都包装在一个函数 func(session sqlx.Session) error {} 中即可,如果事务中的操作返回任何错误, Transact() 都会自动回滚事务。 分布式事务 go-zero 与 dtm 深度合作,原生的支持了分布式事务,详情参见 分布式事务支持 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-lock.html":{"url":"redis-lock.html","title":"redis锁","keywords":"","body":"redis-lock redis lock 既然是锁,首先想到的一个作用就是:防重复点击,在一个时间点只有一个请求产生效果。 而既然是 redis,就得具有排他性,同时也具有锁的一些共性: 高性能 不能出现死锁 不能出现节点down掉后加锁失败 go-zero 中利用 redis set key nx 可以保证key不存在时写入成功,px 可以让key超时后自动删除「最坏情况也就是超时自动删除key,从而也不会出现死锁」 example redisLockKey := fmt.Sprintf(\"%v%v\", redisTpl, headId) // 1. New redislock redisLock := redis.NewRedisLock(redisConn, redisLockKey) // 2. 可选操作,设置 redislock 过期时间 redisLock.SetExpire(redisLockExpireSeconds) if ok, err := redisLock.Acquire(); !ok || err != nil { return nil, errors.New(\"当前有其他用户正在进行操作,请稍后重试\") } defer func() { recover() // 3. 释放锁 redisLock.Release() }() 和你在使用 sync.Mutex 的方式时一致的。加锁解锁,执行你的业务操作。 获取锁 lockCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then redis.call(\"SET\", KEYS[1], ARGV[1], \"PX\", ARGV[2]) return \"OK\" else return redis.call(\"SET\", KEYS[1], ARGV[1], \"NX\", \"PX\", ARGV[2]) end` func (rl *RedisLock) Acquire() (bool, error) { seconds := atomic.LoadUint32(&rl.seconds) // execute luascript resp, err := rl.store.Eval(lockCommand, []string{rl.key}, []string{ rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance)}) if err == red.Nil { return false, nil } else if err != nil { logx.Errorf(\"Error on acquiring lock for %s, %s\", rl.key, err.Error()) return false, err } else if resp == nil { return false, nil } reply, ok := resp.(string) if ok && reply == \"OK\" { return true, nil } else { logx.Errorf(\"Unknown reply when acquiring lock for %s: %v\", rl.key, resp) return false, nil } } 先介绍几个 redis 的命令选项,以下是为 set 命令增加的选项: ex seconds :设置key过期时间,单位s px milliseconds :设置key过期时间,单位毫秒 nx:key不存在时,设置key的值 xx:key存在时,才会去设置key的值 其中 lua script 涉及的入参: args 示例 含义 KEYS[1] key$20201026 redis key ARGV[1] lmnopqrstuvwxyzABCD 唯一标识:随机字符串 ARGV[2] 30000 设置锁的过期时间 然后来说说代码特性: Lua 脚本保证原子性「当然,把多个操作在 Redis 中实现成一个操作,也就是单命令操作」 使用了 set key value px milliseconds nx value 具有唯一性 加锁时首先判断 key 的 value 是否和之前设置的一致,一致则修改过期时间 释放锁 delCommand = `if redis.call(\"GET\", KEYS[1]) == ARGV[1] then return redis.call(\"DEL\", KEYS[1]) else return 0 end` func (rl *RedisLock) Release() (bool, error) { resp, err := rl.store.Eval(delCommand, []string{rl.key}, []string{rl.id}) if err != nil { return false, err } if reply, ok := resp.(int64); !ok { return false, nil } else { return reply == 1, nil } } 释放锁的时候只需要关注一点: 不能释放别人的锁,不能释放别人的锁,不能释放别人的锁 所以需要先 get(key) == value「key」,为 true 才会去 delete Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"periodlimit.html":{"url":"periodlimit.html","title":"periodlimit限流","keywords":"","body":"periodlimit 不管是在单体服务中还是在微服务中,开发者为前端提供的API接口都是有访问上限的,当访问频率或者并发量超过其承受范围时候,我们就必须考虑限流来保证接口的可用性或者降级可用性。即接口也需要安装上保险丝,以防止非预期的请求对系统压力过大而引起的系统瘫痪。 本文就来介绍一下 periodlimit 。 使用 const ( seconds = 1 total = 100 quota = 5 ) // New limiter l := NewPeriodLimit(seconds, quota, redis.NewRedis(s.Addr(), redis.NodeType), \"periodlimit\") // take source code, err := l.Take(\"first\") if err != nil { logx.Error(err) return true } // switch val => process request switch code { case limit.OverQuota: logx.Errorf(\"OverQuota key: %v\", key) return false case limit.Allowed: logx.Infof(\"AllowedQuota key: %v\", key) return true case limit.HitQuota: logx.Errorf(\"HitQuota key: %v\", key) // todo: maybe we need to let users know they hit the quota return false default: logx.Errorf(\"DefaultQuota key: %v\", key) // unknown response, we just let the sms go return true } periodlimit go-zero 采取 滑动窗口 计数的方式,计算一段时间内对同一个资源的访问次数,如果超过指定的 limit ,则拒绝访问。当然如果你是在一段时间内访问不同的资源,每一个资源访问量都不超过 limit ,此种情况是允许大量请求进来的。 而在一个分布式系统中,存在多个微服务提供服务。所以当瞬间的流量同时访问同一个资源,如何让计数器在分布式系统中正常计数? 同时在计算资源访问时,可能会涉及多个计算,如何保证计算的原子性? go-zero 借助 redis 的 incrby 做资源访问计数 采用 lua script 做整个窗口计算,保证计算的原子性 下面来看看 lua script 控制的几个关键属性: argument mean key[1] 访问资源的标示 ARGV[1] limit => 请求总数,超过则限速。可设置为 QPS ARGV[2] window大小 => 滑动窗口,用 ttl 模拟出滑动的效果 -- to be compatible with aliyun redis, -- we cannot use `local key = KEYS[1]` to reuse thekey local limit = tonumber(ARGV[1]) local window = tonumber(ARGV[2]) -- incrbt key 1 => key visis++ local current = redis.call(\"INCRBY\", KEYS[1], 1) -- 如果是第一次访问,设置过期时间 => TTL = window size -- 因为是只限制一段时间的访问次数 if current == 1 then redis.call(\"expire\", KEYS[1], window) return 1 elseif current 至于上述的 return code ,返回给调用方。由调用方来决定请求后续的操作: return code tag call code mean 0 OverQuota 3 over limit 1 Allowed 1 in limit 2 HitQuota 2 hit limit 下面这张图描述了请求进入的过程,以及请求触发 limit 时后续发生的情况: 后续处理 如果在服务某个时间点,请求大批量打进来,periodlimit 短期时间内达到 limit 阈值,而且设置的时间范围还远远没有到达。后续请求的处理就成为问题。 periodlimit 中并没有处理,而是返回 code 。把后续请求的处理交给了开发者自己处理。 如果不做处理,那就是简单的将请求拒绝 如果需要处理这些请求,开发者可以借助 mq 将请求缓冲,减缓请求的压力 采用 tokenlimit,允许暂时的流量冲击 所以下一篇我们就来聊聊 tokenlimit 总结 go-zero 中的 periodlimit 限流方案是基于 redis 计数器,通过调用 redis lua script ,保证计数过程的原子性,同时保证在分布式的情况下计数是正常的。但是这种方案存在缺点,因为它要记录时间窗口内的所有行为记录,如果这个量特别大的时候,内存消耗会变得非常严重。 参考 go-zero periodlimit 分布式服务限流实战,已经为你排好坑了 tokenlimit Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tokenlimit.html":{"url":"tokenlimit.html","title":"令牌桶限流","keywords":"","body":"tokenlimit 本节将通过令牌桶限流(tokenlimit)来介绍其基本使用。 使用 const ( burst = 100 rate = 100 seconds = 5 ) store := redis.NewRedis(\"localhost:6379\", \"node\", \"\") fmt.Println(store.Ping()) // New tokenLimiter limiter := limit.NewTokenLimiter(rate, burst, store, \"rate-test\") timer := time.NewTimer(time.Second * seconds) quit := make(chan struct{}) defer timer.Stop() go func() { tokenlimit 从整体上令牌桶生产token逻辑如下: 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中; 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃; 当流量以速率v进入,从桶中以速率v取令牌,拿到令牌的流量通过,拿不到令牌流量不通过,执行熔断逻辑; go-zero 在两类限流器下都采取 lua script 的方式,依赖redis可以做到分布式限流,lua script同时可以做到对 token 生产读取操作的原子性。 下面来看看 lua script 控制的几个关键属性: argument mean ARGV[1] rate 「每秒生成几个令牌」 ARGV[2] burst 「令牌桶最大值」 ARGV[3] now_time「当前时间戳」 ARGV[4] get token nums 「开发者需要获取的token数」 KEYS[1] 表示资源的tokenkey KEYS[2] 表示刷新时间的key -- 返回是否可以活获得预期的token local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) -- fill_time:需要填满 token_bucket 需要多久 local fill_time = capacity/rate -- 将填充时间向下取整 local ttl = math.floor(fill_time*2) -- 获取目前 token_bucket 中剩余 token 数 -- 如果是第一次进入,则设置 token_bucket 数量为 令牌桶最大值 local last_tokens = tonumber(redis.call(\"get\", KEYS[1])) if last_tokens == nil then last_tokens = capacity end -- 上一次更新 token_bucket 的时间 local last_refreshed = tonumber(redis.call(\"get\", KEYS[2])) if last_refreshed == nil then last_refreshed = 0 end local delta = math.max(0, now-last_refreshed) -- 通过当前时间与上一次更新时间的跨度,以及生产token的速率,计算出新的token数 -- 如果超过 max_burst,多余生产的token会被丢弃 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) local allowed = filled_tokens >= requested local new_tokens = filled_tokens if allowed then new_tokens = filled_tokens - requested end -- 更新新的token数,以及更新时间 redis.call(\"setex\", KEYS[1], ttl, new_tokens) redis.call(\"setex\", KEYS[2], ttl, now) return allowed 上述可以看出 lua script :只涉及对 token 操作,保证 token 生产合理和读取合理。 函数分析 从上述流程中看出: 有多重保障机制,保证限流一定会完成。 如果redis limiter失效,至少在进程内rate limiter兜底。 重试 redis limiter 机制保证尽可能地正常运行。 总结 go-zero 中的 tokenlimit 限流方案适用于瞬时流量冲击,现实请求场景并不以恒定的速率。令牌桶相当预请求,当真实的请求到达不至于瞬间被打垮。当流量冲击到一定程度,则才会按照预定速率进行消费。 但是生产token上,不能按照当时的流量情况作出动态调整,不够灵活,还可以进行进一步优化。此外可以参考Token bucket WIKI 中提到分层令牌桶,根据不同的流量带宽,分至不同排队中。 参考 go-zero tokenlimit Go-Redis 提供的分布式限流库 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"timing-wheel.html":{"url":"timing-wheel.html","title":"时间轮介绍","keywords":"","body":"TimingWheel 本文来介绍 go-zero 中 延迟操作。延迟操作,可以采用两个方案: Timer:定时器维护一个优先队列,到时间点执行,然后把需要执行的 task 存储在 map 中 collection 中的 timingWheel ,维护一个存放任务组的数组,每一个槽都维护一个存储task的双向链表。开始执行时,计时器每隔指定时间执行一个槽里面的tasks。 方案2把维护task从 优先队列 O(nlog(n)) 降到 双向链表 O(1),而执行task也只要轮询一个时间点的tasks O(N),不需要像优先队列,放入和删除元素 O(nlog(n))。 cache 中的 timingWheel 首先我们先来在 collection 的 cache 中关于 timingWheel 的使用: timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) if err != nil { return nil, err } cache.timingWheel = timingWheel 这是 cache 初始化中也同时初始化 timingWheel 做key的过期处理,参数依次代表: interval:时间划分刻度 numSlots:时间槽 execute:时间点执行函数 在 cache 中执行函数则是 删除过期key,而这个过期则由 timingWheel 来控制推进时间。 接下来,就通过 cache 对 timingWheel 的使用来认识。 初始化 // 真正做初始化 func newTimingWheelWithClock(interval time.Duration, numSlots int, execute Execute, ticker timex.Ticker) ( *TimingWheel, error) { tw := &TimingWheel{ interval: interval, // 单个时间格时间间隔 ticker: ticker, // 定时器,做时间推动,以interval为单位推进 slots: make([]*list.List, numSlots), // 时间轮 timers: NewSafeMap(), // 存储task{key, value}的map [执行execute所需要的参数] tickedPos: numSlots - 1, // at previous virtual circle execute: execute, // 执行函数 numSlots: numSlots, // 初始化 slots num setChannel: make(chan timingEntry), // 以下几个channel是做task传递的 moveChannel: make(chan baseEntry), removeChannel: make(chan interface{}), drainChannel: make(chan func(key, value interface{})), stopChannel: make(chan lang.PlaceholderType), } // 把 slot 中存储的 list 全部准备好 tw.initSlots() // 开启异步协程,使用 channel 来做task通信和传递 go tw.run() return tw, nil } 以上比较直观展示 timingWheel 的 “时间轮”,后面会围绕这张图解释其中推进的细节。 go tw.run() 开一个协程做时间推动: func (tw *TimingWheel) run() { for { select { // 定时器做时间推动 -> scanAndRunTasks() case 可以看出,在初始化的时候就开始了 timer 执行,并以interval时间段转动,然后底层不停的获取来自 slot 中的 list 的task,交给 execute 执行。 Task Operation 紧接着就是设置 cache key : func (c *Cache) Set(key string, value interface{}) { c.lock.Lock() _, ok := c.data[key] c.data[key] = value c.lruCache.add(key) c.lock.Unlock() expiry := c.unstableExpiry.AroundDuration(c.expire) if ok { c.timingWheel.MoveTimer(key, expiry) } else { c.timingWheel.SetTimer(key, value, expiry) } } 先看在 data map 中有没有存在这个key 存在,则更新 expire -> MoveTimer() 第一次设置key -> SetTimer() 所以对于 timingWheel 的使用上就清晰了,开发者根据需求可以 add 或是 update。 同时我们跟源码进去会发现:SetTimer() MoveTimer() 都是将task输送到channel,由 run() 中开启的协程不断取出 channel 的task操作。 SetTimer() -> setTask(): not exist task:getPostion -> pushBack to list -> setPosition exist task:get from timers -> moveTask() MoveTimer() -> moveTask() 由上面的调用链,有一个都会调用的函数:moveTask() func (tw *TimingWheel) moveTask(task baseEntry) { // timers: Map => 通过key获取 [positionEntry「pos, task」] val, ok := tw.timers.Get(task.key) if !ok { return } timer := val.(*positionEntry) // {delay 延迟时间比一个时间格间隔还小,没有更小的刻度,说明任务应该立即执行 if task.delay interval,则通过 延迟时间delay 计算其出时间轮中的 new pos, circle pos, circle := tw.getPositionAndCircle(task.delay) if pos >= timer.pos { timer.item.circle = circle // 记录前后的移动offset。为了后面过程重新入队 timer.item.diff = pos - timer.pos } else if circle > 0 { // 转移到下一层,将 circle 转换为 diff 一部分 circle-- timer.item.circle = circle // 因为是一个数组,要加上 numSlots [也就是相当于要走到下一层] timer.item.diff = tw.numSlots + pos - timer.pos } else { // 如果 offset 提前了,此时 task 也还在第一层 // 标记删除老的 task,并重新入队,等待被执行 timer.item.removed = true newItem := &timingEntry{ baseEntry: task, value: timer.item.value, } tw.slots[pos].PushBack(newItem) tw.setTimerPosition(pos, newItem) } } 以上过程有以下几种情况: delay :因为 针对改变的 delay: new >= old: newCircle > 0:计算diff,并将 circle 转换为 下一层,故diff + numslots 如果只是单纯延迟时间缩短,则将老的task标记删除,重新加入list,等待下一轮loop被execute Execute 之前在初始化中,run() 中定时器的不断推进,推进的过程主要就是把 list中的 task 传给执行的 execute func。我们从定时器的执行开始看: // 定时器 「每隔 interval 会执行一次」 func (tw *TimingWheel) onTick() { // 每次执行更新一下当前执行 tick 位置 tw.tickedPos = (tw.tickedPos + 1) % tw.numSlots // 获取此时 tick位置 中的存储task的双向链表 l := tw.slots[tw.tickedPos] tw.scanAndRunTasks(l) } 紧接着是如何去执行 execute: func (tw *TimingWheel) scanAndRunTasks(l *list.List) { // 存储目前需要执行的task{key, value} [execute所需要的参数,依次传递给execute执行] var tasks []timingTask for e := l.Front(); e != nil; { task := e.Value.(*timingEntry) // 标记删除,在 scan 中做真正的删除 「删除map的data」 if task.removed { next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next continue } else if task.circle > 0 { // 当前执行点已经过期,但是同时不在第一层,所以当前层即然已经完成了,就会降到下一层 // 但是并没有修改 pos task.circle-- e = e.Next() continue } else if task.diff > 0 { // 因为之前已经标注了diff,需要再进入队列 next := e.Next() l.Remove(e) pos := (tw.tickedPos + task.diff) % tw.numSlots tw.slots[pos].PushBack(task) tw.setTimerPosition(pos, task) task.diff = 0 e = next continue } // 以上的情况都是不能执行的情况,能够执行的会被加入tasks中 tasks = append(tasks, timingTask{ key: task.key, value: task.value, }) next := e.Next() l.Remove(e) tw.timers.Del(task.key) e = next } // for range tasks,然后把每个 task->execute 执行即可 tw.runTasks(tasks) } 具体的分支情况在注释中说明了,在看的时候可以和前面的 moveTask() 结合起来,其中 circle 下降,diff 的计算是关联两个函数的重点。 至于 diff 计算就涉及到 pos, circle 的计算: // interval: 4min, d: 60min, numSlots: 16, tickedPos = 15 // step = 15, pos = 14, circle = 0 func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) { steps := int(d / tw.interval) pos = (tw.tickedPos + steps) % tw.numSlots circle = (steps - 1) / tw.numSlots return } 上面的过程可以简化成下面: steps = d / interval pos = step % numSlots - 1 circle = (step - 1) / numSlots 总结 timingWheel 靠定时器推动,时间前进的同时会取出当前时间格中 list「双向链表」的task,传递到 execute 中执行。 而时间分隔上,时间轮有 circle 分层,这样就可以不断复用原有的 numSlots ,因为定时器在不断 loop,而执行可以把上层的 slot 下降到下层,在不断 loop 中就可以执行到上层的task。 在 go-zero 中还有很多实用的组件工具,用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"eco.html":{"url":"eco.html","title":"go-zero 生态","keywords":"","body":"go-zero 生态 工具中心 intellij插件 vscode插件 分布式事务支持 插件中心 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tool-center.html":{"url":"tool-center.html","title":"工具中心","keywords":"","body":"工具中心 在go-zero中,提供了很多提高工程效率的工具,如api,rpc生成,在此基础之上,api文件的编写就显得那么的无力, 因为缺少了高亮,代码提示,模板生成等,本节将带你了解go-zero是怎么解决这些难题的,本节包含以下小节: intellij插件 vscode插件 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"intellij.html":{"url":"intellij.html","title":"intellij插件","keywords":"","body":"intellij插件 Go-Zero Plugin 介绍 一款支持go-zero api语言结构语法高亮、检测以及api、rpc、model快捷生成的插件工具。 idea版本要求 IntelliJ 2019.3+ (Ultimate or Community) Goland 2019.3+ WebStorm 2019.3+ PhpStorm 2019.3+ PyCharm 2019.3+ RubyMine 2019.3+ CLion 2019.3+ 版本特性 api语法高亮 api语法、语义检测 struct、route、handler重复定义检测 type跳转到类型声明位置 上下文菜单中支持api、rpc、mode相关menu选项 代码格式化(option+command+L) 代码提示 安装方式 方式一 在github的release中找到最新的zip包,下载本地安装即可。(无需解压) 方式二 在plugin商店中,搜索Goctl安装即可 预览 新建 Api(Proto) file 在工程区域目标文件夹右键->New-> New Api(Proto) File ->Empty File/Api(Proto) Template,如图: 快速生成api/rpc服务 在目标文件夹右键->New->Go Zero -> Api Greet Service/Rpc Greet Service Api/Rpc/Model Code生成 方法一(工程区域) 对应文件(api、proto、sql)右键->New->Go Zero-> Api/Rpc/Model Code,如图: 方法二(编辑区域) 对应文件(api、proto、sql)右键-> Generate-> Api/Rpc/Model Code 错误提示 Live Template Live Template可以加快我们对api文件的编写,比如我们在go文件中输入main关键字根据tip回车后会插入一段模板代码 func main(){ } 或者说看到下图你会更加熟悉,曾几何时你还在这里定义过template 下面就进入今天api语法中的模板使用说明吧,我们先来看看service模板的效果 首先上一张图了解一下api文件中几个模板生效区域(psiTree元素区域) 预设模板及生效区域 模板关键字 psiTree生效区域 描述 @doc ApiService doc注释模板 doc ApiService doc注释模板 struct Struct struct声明模板 info ApiFile info block模板 type ApiFile type group模板 handler ApiService handler文件名模板 get ApiService get方法路由模板 head ApiService head方法路由模板 post ApiService post方法路由模板 put ApiService put方法路由模板 delete ApiService delete方法路由模板 connect ApiService connect方法路由模板 options ApiService options方法路由模板 trace ApiService trace方法路由模板 service ApiFile service服务block模板 json Tag、Tag literal tag模板 xml Tag、Tag literal tag模板 path Tag、Tag literal tag模板 form Tag、Tag literal tag模板 关于每个模板对应内容可在Goland(mac Os)->Preference->Editor->Live Templates-> Api|Api Tags中查看详细模板内容,如json tag模板内容为 json:\"$FIELD_NAME$\" Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"vscode.html":{"url":"vscode.html","title":"vscode插件","keywords":"","body":"vs code 插件 该插件可以安装在 1.46.0+ 版本的 Visual Studio Code 上,首先请确保你的 Visual Studio Code 版本符合要求,并已安装 goctl 命令行工具。如果尚未安装 Visual Studio Code,请安装并打开 Visual Studio Code。 导航到“扩展”窗格,搜索 goctl 并安装此扩展(发布者ID为 “xiaoxin-technology.goctl”)。 Visual Studio Code 扩展使用请参考这里。 功能列表 已实现功能 语法高亮 跳转到定义/引用 代码格式化 代码块提示 未实现功能: 语法错误检查 跨文件代码跳转 goctl 命令行调用 语法高亮 代码跳转 代码格式化 调用 goctl 命令行格式化工具,使用前请确认 goctl 已加入 $PATH 且有可执行权限 代码块提示 info 代码块 type 代码块 service 代码块 handler 代码块 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"distributed-transaction.html":{"url":"distributed-transaction.html","title":"分布式事务支持","keywords":"","body":"分布式事务支持 需求场景 在微服务架构中,当我们需要跨服务保证数据一致性时,原先的数据库事务力不从心,无法将跨库、跨服务的多个操作放在一个事务中。这样的应用场景非常多,我们可以列举出很多: 订单系统:需要保证创建订单和扣减库存要么同时成功,要么同时回滚 跨行转账场景:数据不在一个数据库,但需要保证余额扣减和余额增加要么同时成功,要么同时失败 积分兑换场景:需要保证积分扣减和权益增加同时成功,或者同时失败 出行订票场景:需要在第三方系统同时定几张票,要么同时成功,要么全部取消 面对这些本地事务无法解决的场景,我们需要分布式事务的解决方案,保证跨服务、跨数据库更新数据的一致性。 解决方案 go-zero与dtm强强联合,推出了在go-zero中无缝接入dtm的极简方案,是go生态中首家提供分布式事务能力的微服务框架。该方案具备以下特征: dtm服务可以通过配置,直接注册到go-zero的注册中心 go-zero能够以内建的target格式访问dtm服务器 dtm能够识别go-zero的target格式,动态访问go-zero中的服务 详细的接入方式,参见dtm文档:go-zero支持 更多应用场景 dtm不仅可以解决上述的分布式事务场景,还可以解决更多的与数据一致性相关的场景,包括: 数据库与缓存一致性: dtm 的二阶段消息,能够保证数据库更新操作,和缓存更新/删除操作的原子性 秒杀系统: dtm 能够保证秒杀场景下,创建的订单量与库存扣减数量完全一样,无需后续的人工校准 多种存储组合: dtm 已支持数据库、Redis、Mongo等多种存储,可以将它们组合为一个全局事务,保证数据的一致性 更多 dtm 的能力和介绍,参见dtm Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"plugin-center.html":{"url":"plugin-center.html","title":"插件中心","keywords":"","body":"插件中心 goctl api提供了对plugin命令来支持对api进行功能扩展,当goctl api中的功能不满足你的使用, 或者需要对goctl api进行功能自定义的扩展,那么插件功能将非常适合开发人员进行自给自足,详情见 goctl plugin 插件资源 goctl-go-compact goctl默认的一个路由一个文件合并成一个文件 goctl-swagger 通过api文件生成swagger文档 goctl-php goctl-php是一款基于goctl的插件,用于生成 php 调用端(服务端) http server请求代码 猜你想看 goctl插件 api语法介绍 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"learning-resource.html":{"url":"learning-resource.html","title":"学习资源","keywords":"","body":"学习资源 这里将不定期更新go-zero的最新学习资源通道,目前包含通道有: 公众号 Go夜读 Go开源说 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"wechat.html":{"url":"wechat.html","title":"公众号","keywords":"","body":"公众号 微服务实践是go-zero的官方公众号,在这里会发布最新的go-zero最佳实践,同步go夜读、go开源说、GopherChina、腾讯云开发者大会等多渠道关于go-zero的最新技术和资讯。 公众号名称 公众号作者 公众号二维码 微服务实践 kevwan 推荐主题 《带你十天轻松搞定 Go 微服务》 该系列将带你在本机利用 go-zero 快速开发一个商城系统,向大家详细展示了基于 go-zero 框架构建微服务的过程,整个系列分十篇文章,目录结构如下: 第一天:环境搭建 第二天:服务拆分 第三天:用户服务 第四天:产品服务 第五天:订单服务 第六天:支付服务 第七天:RPC服务Auth验证 第八天:服务监控 第九天:链路追踪 第十天:分布式事务 干货 这里列举一些干货,想要收获更多go-zero最佳实践干货,可以关注公众号获取最新动态。 《一文读懂云原生 go-zero 微服务框架》 《你还在手撕微服务?快试试 go-zero 的微服务自动生成》 《最简单的Go Dockerfile编写姿势,没有之一!》 《通过MapReduce降低服务响应时间》 《微服务过载保护原理与实战 《最简单的 K8S 部署文件编写姿势,没有之一!》 《go-zero 如何应对海量定时/延迟任务?》 《go-zero 如何扛住流量冲击(一)》 《服务自适应降载保护设计》 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"goreading.html":{"url":"goreading.html","title":"Go夜读","keywords":"","body":"Go夜读 2020-08-16 晓黑板 go-zero 微服务框架的架构设计 2020-10-03 go-zero 微服务框架和线上交流 防止缓存击穿之进程内共享调用 基于go-zero实现JWT认证 再见go-micro!企业项目迁移go-zero全攻略(一) Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"gotalk.html":{"url":"gotalk.html","title":"Go开源说","keywords":"","body":"Go开源说 Go 开源说第四期 - Go-Zero Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"practise.html":{"url":"practise.html","title":"User Practise","keywords":"","body":"User Practise [!TIP] This document is machine-translated by Google. If you find grammatical and semantic errors, and the document description is not clear, please PR Persistent layer cache Business layer cache Queue Middle Ground System Stream Handler Online Exchange Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"redis-cache.html":{"url":"redis-cache.html","title":"go-zero缓存设计之持久层缓存","keywords":"","body":"go-zero缓存设计之持久层缓存 缓存设计原理 我们对缓存是只删除,不做更新,一旦DB里数据出现修改,我们就会直接删除对应的缓存,而不是去更新。 我们看看删除缓存的顺序怎样才是正确的。 先删除缓存,再更新DB 我们看两个并发请求的情况,A请求需要更新数据,先删除了缓存,然后B请求来读取数据,此时缓存没有数据,就会从DB加载数据并写回缓存,然后A更新了DB,那么此时缓存内的数据就会一直是脏数据,直到缓存过期或者有新的更新数据的请求。如图 先更新DB,再删除缓存 A请求先更新DB,然后B请求来读取数据,此时返回的是老数据,此时可以认为是A请求还没更新完,最终一致性,可以接受,然后A删除了缓存,后续请求都会拿到最新数据,如图 让我们再来看一下正常的请求流程: 第一个请求更新DB,并删除了缓存 第二个请求读取缓存,没有数据,就从DB读取数据,并回写到缓存里 后续读请求都可以直接从缓存读取 我们再看一下DB查询有哪些情况,假设行记录里有ABCDEFG七列数据: 只查询部分列数据的请求,比如请求其中的ABC,CDE或者EFG等,如图 查询单条完整行记录,如图 查询多条行记录的部分或全部列,如图 对于上面三种情况,首先,我们不用部分查询,因为部分查询没法缓存,一旦缓存了,数据有更新,没法定位到有哪些数据需要删除;其次,对于多行的查询,根据实际场景和需要,我们会在业务层建立对应的从查询条件到主键的映射;而对于单行完整记录的查询,go-zero 内置了完整的缓存管理方式。所以核心原则是:go-zero 缓存的一定是完整的行记录。 下面我们来详细介绍 go-zero 内置的三种场景的缓存处理方式: 基于主键的缓存PRIMARY KEY (`id`) 这种相对来讲是最容易处理的缓存,只需要在 redis 里用 primary key 作为 key 来缓存行记录即可。 基于唯一索引的缓存 在做基于索引的缓存设计的时候我借鉴了 database 索引的设计方法,在 database 设计里,如果通过索引去查数据时,引擎会先在 索引->主键 的 tree 里面查找到主键,然后再通过主键去查询行记录,就是引入了一个间接层去解决索引到行记录的对应问题。在 go-zero 的缓存设计里也是同样的原理。 基于索引的缓存又分为单列唯一索引和多列唯一索引: 但是对于 go-zero 来说,单列和多列只是生成缓存 key 的方式不同而已,背后的控制逻辑是一样的。然后 go-zero 内置的缓存管理就比较好的控制了数据一致性问题,同时也内置防止了缓存的击穿、穿透、雪崩问题(这些在 gopherchina 大会上分享的时候仔细讲过,见后续 gopherchina 分享视频)。 另外,go-zero 内置了缓存访问量、访问命中率统计,如下所示: dbcache(sqlc) - qpm: 5057, hit_ratio: 99.7%, hit: 5044, miss: 13, db_fails: 0 可以看到比较详细的统计信息,便于我们来分析缓存的使用情况,对于缓存命中率极低或者请求量极小的情况,我们就可以去掉缓存了,这样也可以降低成本。 单列唯一索引如下: UNIQUE KEY `product_idx` (`product`) 多列唯一索引如下: UNIQUE KEY `vendor_product_idx` (`vendor`, `product`) 缓存代码解读 1.基于主键的缓存逻辑 具体实现代码如下: func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error { return cc.cache.Take(v, key, func(v interface{}) error { return query(cc.db, v) }) } 这里的 Take 方法是先从缓存里去通过 key 拿数据,如果拿到就直接返回,如果拿不到,那么就通过 query 方法去 DB 读取完整行记录并写回缓存,然后再返回数据。整个逻辑还是比较简单易懂的。 我们详细看看 Take 的实现: func (c cacheNode) Take(v interface{}, key string, query func(v interface{}) error) error { return c.doTake(v, key, query, func(v interface{}) error { return c.SetCache(key, v) }) } Take 的逻辑如下: 用 key 从缓存里查找数据 如果找到,则返回数据 如果找不到,用 query 方法去读取数据 读到后调用 c.SetCache(key, v) 设置缓存 其中的 doTake 代码和解释如下: // v - 需要读取的数据对象 // key - 缓存key // query - 用来从DB读取完整数据的方法 // cacheVal - 用来写缓存的方法 func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) error, cacheVal func(v interface{}) error) error { // 用barrier来防止缓存击穿,确保一个进程内只有一个请求去加载key对应的数据 val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { // 从cache里读取数据 if err := c.doGetCache(key, v); err != nil { // 如果是预先放进来的placeholder(用来防止缓存穿透)的,那么就返回预设的errNotFound // 如果是未知错误,那么就直接返回,因为我们不能放弃缓存出错而直接把所有请求去请求DB, // 这样在高并发的场景下会把DB打挂掉的 if err == errPlaceholder { return nil, c.errNotFound } else if err != c.errNotFound { // why we just return the error instead of query from db, // because we don't allow the disaster pass to the DBs. // fail fast, in case we bring down the dbs. return nil, err } // 请求DB // 如果返回的error是errNotFound,那么我们就需要在缓存里设置placeholder,防止缓存穿透 if err = query(v); err == c.errNotFound { if err = c.setCacheWithNotFound(key); err != nil { logx.Error(err) } return nil, c.errNotFound } else if err != nil { // 统计DB失败 c.stat.IncrementDbFails() return nil, err } // 把数据写入缓存 if err = cacheVal(v); err != nil { logx.Error(err) } } // 返回json序列化的数据 return jsonx.Marshal(v) }) if err != nil { return err } if fresh { return nil } // got the result from previous ongoing query c.stat.IncrementTotal() c.stat.IncrementHit() // 把数据写入到传入的v对象里 return jsonx.Unmarshal(val.([]byte), v) } 2. 基于唯一索引的缓存逻辑 因为这块比较复杂,所以我用不同颜色标识出来了响应的代码块和逻辑,block 2 其实跟基于主键的缓存是一样的,这里主要讲 block 1 的逻辑。 代码块的 block 1 部分分为两种情况: 通过索引能够从缓存里找到主键,此时就直接用主键走 block 2 的逻辑了,后续同上面基于主键的缓存逻辑 通过索引无法从缓存里找到主键 通过索引从DB里查询完整行记录,如有 error,返回 查到完整行记录后,会把主键到完整行记录的缓存和索引到主键的缓存同时写到 redis 里 返回所需的行记录数据 // v - 需要读取的数据对象 // key - 通过索引生成的缓存key // keyer - 用主键生成基于主键缓存的key的方法 // indexQuery - 用索引从DB读取完整数据的方法,需要返回主键 // primaryQuery - 用主键从DB获取完整数据的方法 func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error { var primaryKey interface{} var found bool // 先通过索引查询缓存,看是否有索引到主键的缓存 if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) { // 如果没有索引到主键的缓存,那么就通过索引查询完整数据 primaryKey, err = indexQuery(cc.db, v) if err != nil { return } // 通过索引查询到了完整数据,设置found,后面直接使用,不需要再从缓存读取数据了 found = true // 将主键到完整数据的映射保存到缓存里,TakeWithExpire方法已经将索引到主键的映射保存到缓存了 return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary) }); err != nil { return err } // 已经通过索引找到了数据,直接返回即可 if found { return nil } // 通过主键从缓存读取数据,如果缓存没有,通过primaryQuery方法从DB读取并回写缓存再返回数据 return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error { return primaryQuery(cc.db, v, primaryKey) }) } 我们来看一个实际的例子 func (m *defaultUserModel) FindOneByUser(user string) (*User, error) { var resp User // 生成基于索引的key indexKey := fmt.Sprintf(\"%s%v\", cacheUserPrefix, user) err := m.QueryRowIndex(&resp, indexKey, // 基于主键生成完整数据缓存的key func(primary interface{}) string { return fmt.Sprintf(\"user#%v\", primary) }, // 基于索引的DB查询方法 func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { query := fmt.Sprintf(\"select %s from %s where user = ? limit 1\", userRows, m.table) if err := conn.QueryRow(&resp, query, user); err != nil { return nil, err } return resp.Id, nil }, // 基于主键的DB查询方法 func(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf(\"select %s from %s where id = ?\", userRows, m.table) return conn.QueryRow(&resp, query, primary) }) // 错误处理,需要判断是否返回的是sqlc.ErrNotFound,如果是,我们用本package定义的ErrNotFound返回 // 避免使用者感知到有没有使用缓存,同时也是对底层依赖的隔离 switch err { case nil: return &resp, nil case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err } } 所有上面这些缓存的自动管理代码都是可以通过 goctl 自动生成的,我们团队内部 CRUD 和缓存基本都是通过 goctl 自动生成的,可以节省大量开发时间,并且缓存代码本身也是非常容易出错的,即使有很好的代码经验,也很难每次完全写对,所以我们推荐尽可能使用自动的缓存代码生成工具去避免错误。 猜你想看 Go开源说第四期-go-zero缓存如何设计 Goctl Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"buiness-cache.html":{"url":"buiness-cache.html","title":"go-zero缓存设计之业务层缓存","keywords":"","body":"go-zero缓存设计之业务层缓存 在上一篇go-zero缓存设计之持久层缓存介绍了db层缓存,回顾一下,db层缓存主要设计可以总结为: 缓存只删除不更新 行记录始终只存储一份,即主键对应行记录 唯一索引仅缓存主键值,不直接缓存行记录(参考mysql索引思想) 防缓存穿透设计,默认一分钟 不缓存多行记录 前言 在大型业务系统中,通过对持久层添加缓存,对于大多数单行记录查询,相信缓存能够帮持久层减轻很大的访问压力,但在实际业务中,数据读取不仅仅只是单行记录, 面对大量多行记录的查询,这对持久层也会造成不小的访问压力,除此之外,像秒杀系统、选课系统这种高并发的场景,单纯靠持久层的缓存是不现实的,本节我们来 介绍go-zero实践中的缓存设计——biz缓存。 适用场景举例 选课系统 内容社交系统 秒杀 ... 像这些系统,我们可以在业务层再增加一层缓存来存储系统中的关键信息,如选课系统中学生选课信息,课程剩余名额;内容社交系统中某一段时间之间的内容信息等。 接下来,我们一内容社交系统来进行举例说明。 在内容社交系统中,我们一般是先查询一批内容列表,然后点击某条内容查看详情, 在没有添加biz缓存前,内容信息的查询流程图应该为: 从图以及上一篇文章go-zero缓存设计之持久层缓存中我们可以知道,内容列表的获取是没办法依赖缓存的, 如果我们在业务层添加一层缓存用来存储列表中的关键信息(甚至完整信息),那么多行记录的访问不在是一个问题,这就是biz redis要做的事情。 接下来我们来看一下设计方案,假设内容系统中单行记录包含以下字段 字段名称 字段类型 备注 id string 内容id title string 标题 content string 详细内容 createTime time.Time 创建时间 我们的目标是获取一批内容列表,而尽量避免内容列表走db造成访问压力,首先我们采用redis的sort set数据结构来存储,根需要存储的字段信息量,有两种redis存储方案: 缓存局部信息 对其关键字段信息(如:id等)按照一定规则压缩,并存储,score我们用createTime毫秒值(时间值相等这里不讨论),这种存储方案的好处是节约redis存储空间, 那另一方面,缺点就是需要对列表详细内容进行二次回查(但这次回查是会利用到持久层的行记录缓存的) 缓存完整信息 对发布的所有内容按照一定规则压缩后均进行存储,同样score我们还是用createTime毫秒值,这种存储方案的好处是业务的增、删、查、改均走redis,而db层这时候 就可以不用考虑行记录缓存了,持久层仅提供数据备份和恢复使用,从另一方面来看,其缺点也很明显,需要的存储空间、配置要求更高,费用也会随之增大。 示例代码: type Content struct { Id string `json:\"id\"` Title string `json:\"title\"` Content string `json:\"content\"` CreateTime time.Time `json:\"create_time\"` } const bizContentCacheKey = `biz#content#cache` // AddContent 提供内容存储 func AddContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zadd(bizContentCacheKey, c.CreateTime.UnixNano()/1e6, v) return err } // DelContent 提供内容删除 func DelContent(r redis.Redis, c *Content) error { v := compress(c) _, err := r.Zrem(bizContentCacheKey, v) return err } // 内容压缩 func compress(c *Content) string { // todo: do it yourself var ret string return ret } // 内容解压 func unCompress(v string) *Content { // todo: do it yourself var ret Content return &ret } // ListByRangeTime提供根据时间段进行数据查询 func ListByRangeTime(r redis.Redis, start, end time.Time) ([]*Content, error) { kvs, err := r.ZrangebyscoreWithScores(bizContentCacheKey, start.UnixNano()/1e6, end.UnixNano()/1e6) if err != nil { return nil, err } var list []*Content for _, kv := range kvs { data:=unCompress(kv.Key) list = append(list, data) } return list, nil } 在以上例子中,redis是没有设置过期时间的,我们将增、删、改、查操作均同步到redis,我们认为内容社交系统的列表访问请求是比较高的情况下才做这样的方案设计, 除此之外,还有一些数据访问,没有想内容设计系统这么频繁的访问, 可能是某一时间段内访问量突如其来的增加,之后可能很长一段时间才会再访问一次,以此间隔, 或者说不会再访问了,面对这种场景,如果我又该如何考虑缓存的设计呢?在go-zero内容实践中,有两种方案可以解决这种问题: 增加内存缓存:通过内存缓存来存储当前可能突发访问量比较大的数据,常用的存储方案采用map数据结构来存储,map数据存储实现比较简单,但缓存过期处理则需要增加 定时器来出来,另一宗方案是通过go-zero库中的 Cache ,其是专门 用于内存管理. 采用biz redis,并设置合理的过期时间 总结 以上两个场景可以包含大部分的多行记录缓存,对于多行记录查询量不大的场景,暂时没必要直接把biz redis放进去,可以先尝试让db来承担,开发人员可以根据持久层监控及服务 监控来衡量时候需要引入biz。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-queue.html":{"url":"go-queue.html","title":"go-zero分布式定时任务","keywords":"","body":" go-zero 分布式定时任务 日常任务开发中,我们会有很多异步、批量、定时、延迟任务要处理,go-zero中有go-queue,推荐使用go-queue去处理,go-queue本身也是基于go-zero开发的,其本身是有两种模式 dq : 依赖于beanstalkd,分布式,可存储,延迟、定时设置,关机重启可以重新执行,消息不会丢失,使用非常简单,go-queue中使用了redis setnx保证了每条消息只被消费一次,使用场景主要是用来做日常任务使用 kq:依赖于kafka,这个就不多介绍啦,大名鼎鼎的kafka,使用场景主要是做消息队列 我们主要说一下dq,kq使用也一样的,只是依赖底层不同,如果没使用过beanstalkd,没接触过beanstalkd的可以先google一下,使用起来还是挺容易的。 etc/job.yaml : 配置文件 Name: job Log: ServiceName: job Level: info #dq依赖Beanstalks、redis ,Beanstalks配置、redis配置 DqConf: Beanstalks: - Endpoint: 127.0.0.1:7771 Tube: tube1 - Endpoint: 127.0.0.1:7772 Tube: tube2 Redis: Host: 127.0.0.1:6379 Type: node Internal/config/config.go :解析dq对应etc/*.yaml配置 /** * @Description 配置文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package config import ( \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/service\" ) type Config struct { service.ServiceConf DqConf dq.DqConf } Handler/router.go : 负责注册多任务 /** * @Description 注册job * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package handler import ( \"context\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/logic\" \"job/internal/svc\" ) func RegisterJob(serverCtx *svc.ServiceContext,group *service.ServiceGroup) { group.Add(logic.NewProducerLogic(context.Background(),serverCtx)) group.Add(logic.NewConsumerLogic(context.Background(),serverCtx)) } ProducerLogic: 其中一个job业务逻辑 /** * @Description 生产者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-queue/dq\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" \"strconv\" \"time\" ) type Producer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewProducerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Producer { return &Producer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Producer)Start() { logx.Infof(\"start Producer \\n\") threading.GoSafe(func() { producer := dq.NewProducer([]dq.Beanstalk{ { Endpoint: \"localhost:7771\", Tube: \"tube1\", }, { Endpoint: \"localhost:7772\", Tube: \"tube2\", }, }) for i := 1000; i 另外一个Job业务逻辑 /** * @Description 消费者任务 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package logic import ( \"context\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/threading\" \"job/internal/svc\" ) type Consumer struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger } func NewConsumerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Consumer { return &Consumer{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), } } func (l *Consumer)Start() { logx.Infof(\"start consumer \\n\") threading.GoSafe(func() { l.svcCtx.Consumer.Consume(func(body []byte) { logx.Infof(\"consumer job %s \\n\" ,string(body)) }) }) } func (l *Consumer)Stop() { logx.Infof(\"stop consumer \\n\") } svc/servicecontext.go /** * @Description 配置 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package svc import ( \"job/internal/config\" \"github.com/zeromicro/go-queue/dq\" ) type ServiceContext struct { Config config.Config Consumer dq.Consumer } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Consumer: dq.NewConsumer(c.DqConf), } } main.go启动文件 /** * @Description 启动文件 * @Author Mikael * @Email 13247629622@163.com * @Date 2021/1/18 12:05 * @Version 1.0 **/ package main import ( \"flag\" \"fmt\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/service\" \"job/internal/config\" \"job/internal/handler\" \"job/internal/svc\" \"os\" \"os/signal\" \"syscall\" \"time\" ) var configFile = flag.String(\"f\", \"etc/job.yaml\", \"the config file\") func main() { flag.Parse() //配置 var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) //注册job group := service.NewServiceGroup() handler.RegisterJob(ctx,group) //捕捉信号 ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) go func() { for { s := 常见问题: 为什么使用dq,需要使用redis? 因为beanstalk是单点服务,无法保证高可用。dq可以使用多个单点beanstalk服务,互相备份 & 保证高可用。使用redis解决重复消费问题。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"datacenter.html":{"url":"datacenter.html","title":"我是如何用go-zero 实现一个中台系统","keywords":"","body":"我是如何用 go-zero 实现一个中台系统 作者:Jack Luo 原文连接:https://www.cnblogs.com/jackluo/p/14148518.html [TOC] 最近发现golang社区里出了一个新星的微服务框架,来自好未来,光看这个名字,就很有奔头,之前,也只是玩过go-micro,其实真正的还没有在项目中运用过,只是觉得 微服务,grpc 这些很高大尚,还没有在项目中,真正的玩过,我看了一下官方提供的工具真的很好用,只需要定义好,舒适文件jia结构 都生成了,只需要关心业务,加上最近 有个投票的活动,加上最近这几年中台也比较火,所以决定玩一下, 开源地址: https://github.com/jackluo2012/datacenter 先聊聊中台架构思路吧: 中台的概念大概就是把一个一个的app 统一起来,反正我是这样理解的。 先聊用户服务吧,现在一个公司有很多的公众号、小程序、微信的、支付宝的,还有 xxx xxx,很多的平台,每次开发的时候,我们总是需要做用户登陆的服务,不停的复制代码,然后我们就在思考能不能有一套独立的用户服务,只需要告诉我你需要传个你要登陆的平台(比如微信),微信登陆,需要的是客户端返回给服务端一个code ,然后服务端拿着这个code去微信获取用户信息,反正大家都明白。 我们决定,将所有的信息弄到配置公共服务中去,里面再存微信、支付宝以及其它平台的appid、appkey、还有支付的appid、appkey,这样就写一套。 最后说说实现吧,整个就一个repo: 网关,我们用的是: go-zero的Api服务 其它它的是服务,我们就是用的go-zero的rpc服务 看下目录结构 整个项目完成,我一个人操刀,写了1个来星期,我就实现了上面的中台系统。 datacenter-api服务 先看官方文档 https://go-zero.dev/cn/ 我们先把网关搭建起来: ➜ blogs mkdir datacenter && cd datacenter ➜ datacenter go mod init datacenter go: creating new go.mod: module datacenter ➜ datacenter 查看book目录: ➜ datacenter tree . └── go.mod 0 directories, 1 file 创建api文件 ➜ datacenter goctl api -o datacenter.api Done. ➜ datacenter tree . ├── datacenter.api ├── user.api #用户 ├── votes.api #投票 ├── search.api #搜索 ├── questions.api #问答 └── go.mod 定义api服务 分别包含了上面的 公共服务,用户服务,投票活动服务 datacenter.api的内容: info( title: \"中台系统\"// TODO: add title desc: \"中台系统\"// TODO: add description author: \"jackluo\" email: \"net.webjoy@gmail.com\" ) import \"user.api\" import \"votes.api\" import \"search.api\" import \"questions.api\" //获取 应用信息 type Beid { Beid int64 `json:\"beid\"` } type Token { Token string `json:\"token\"` } type WxTicket { Ticket string `json:\"ticket\"` } type Application { Sname string `json:\"Sname\"` //名称 Logo string `json:\"logo\"` // login Isclose int64 `json:\"isclose\"` //是否关闭 Fullwebsite string `json:\"fullwebsite\"` // 全站名称 } type SnsReq { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 BackUrl string `json:\"back_url\"` //登陆返回的地址 } type SnsResp { Beid Ptyid int64 `json:\"ptyid\"` //对应平台 Appid string `json:\"appid\"` //sns 平台的id Title string `json:\"title\"` //名称 LoginUrl string `json:\"login_url\"` //微信登陆的地址 } type WxShareResp { Appid string `json:\"appid\"` Timestamp int64 `json:\"timestamp\"` Noncestr string `json:\"noncestr\"` Signature string `json:\"signature\"` } @server( group: common ) service datacenter-api { @doc( summary: \"获取站点的信息\" ) @handler appInfo get /common/appinfo (Beid) returns (Application) @doc( summary: \"获取站点的社交属性信息\" ) @handler snsInfo post /common/snsinfo (SnsReq) returns (SnsResp) //获取分享的 @handler wxTicket post /common/wx/ticket (SnsReq) returns (WxShareResp) } //上传需要登陆 @server( jwt: Auth group: common ) service datacenter-api { @doc( summary: \"七牛上传凭证\" ) @handler qiuniuToken post /common/qiuniu/token (Beid) returns (Token) } user.api内容 //注册请求 type RegisterReq struct { // TODO: add members here and delete this comment Mobile string `json:\"mobile\"` //基本一个手机号码就完事 Password string `json:\"password\"` Smscode string `json:\"smscode\"` //短信码 } //登陆请求 type LoginReq struct{ Mobile string `json:\"mobile\"` Type int64 `json:\"type\"` //1.密码登陆,2.短信登陆 Password string `json:\"password\"` } //微信登陆 type WxLoginReq struct { Beid int64 `json:\"beid\"` //应用id Code string `json:\"code\"` //微信登陆密钥 Ptyid int64 `json:\"ptyid\"` //对应平台 } //返回用户信息 type UserReply struct { Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Username string `json:\"username\"` Mobile string `json:\"mobile\"` Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } //返回APPUser type AppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` } type LoginAppUser struct{ Uid int64 `json:\"uid\"` Auid int64 `json:\"auid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 Nickname string `json:\"nickname\"` Openid string `json:\"openid\"` Avator string `json:\"avator\"` JwtToken } type JwtToken struct { AccessToken string `json:\"access_token,omitempty\"` AccessExpire int64 `json:\"access_expire,omitempty\"` RefreshAfter int64 `json:\"refresh_after,omitempty\"` } type UserReq struct{ Auid int64 `json:\"auid\"` Uid int64 `json:\"uid\"` Beid int64 `json:\"beid\"` //应用id Ptyid int64 `json:\"ptyid\"` //对应平台 } type Request { Name string `path:\"name,options=you|me\"` } type Response { Message string `json:\"message\"` } @server( group: user ) service datacenter-api { @handler ping post /user/ping () @handler register post /user/register (RegisterReq) returns (UserReply) @handler login post /user/login (LoginReq) returns (UserReply) @handler wxlogin post /user/wx/login (WxLoginReq) returns (LoginAppUser) @handler code2Session get /user/wx/login () returns (LoginAppUser) } @server( jwt: Auth group: user middleware: Usercheck ) service datacenter-api { @handler userInfo get /user/dc/info (UserReq) returns (UserReply) } votes.api 投票内容 // 投票活动api type Actid struct { Actid int64 `json:\"actid\"` //活动id } type VoteReq struct { Aeid int64 `json:\"aeid\"` // 作品id Actid } type VoteResp struct { VoteReq Votecount int64 `json:\"votecount\"` //投票票数 Viewcount int64 `json:\"viewcount\"` //浏览数 } // 活动返回的参数 type ActivityResp struct { Actid int64 `json:\"actid\"` Title string `json:\"title\"` //活动名称 Descr string `json:\"descr\"` //活动描述 StartDate int64 `json:\"start_date\"` //活动时间 EnrollDate int64 `json:\"enroll_date\"` //投票时间 EndDate int64 `json:\"end_date\"` //活动结束时间 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 Type int64 `json:\"type\"` //投票方式 Num int64 `json:\"num\"` //投票几票 } //报名 type EnrollReq struct { Actid Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 } // 作品返回 type EnrollResp struct { Actid Aeid int64 `json:\"aeid\"` // 作品id Name string `json:\"name\"` // 名称 Address string `json:\"address\"` //地址 Images []string `json:\"images\"` //作品图片 Descr string `json:\"descr\"` // 作品描述 Votecount int64 `json:\"votecount\"` //当前活动的总票数 Viewcount int64 `json:\"viewcount\"` //当前活动的总浏览数 } @server( group: votes ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activityInfo get /votes/activity/info (Actid) returns (ActivityResp) @doc( summary: \"活动访问+1\" ) @handler activityIcrView get /votes/activity/view (Actid) returns (ActivityResp) @doc( summary: \"获取报名的投票作品信息\" ) @handler enrollInfo get /votes/enroll/info (VoteReq) returns (EnrollResp) @doc( summary: \"获取报名的投票作品列表\" ) @handler enrollLists get /votes/enroll/lists (Actid) returns(EnrollResp) } @server( jwt: Auth group: votes middleware: Usercheck ) service datacenter-api { @doc( summary: \"投票\" ) @handler vote post /votes/vote (VoteReq) returns (VoteResp) @handler enroll post /votes/enroll (EnrollReq) returns (EnrollResp) } questions.api 问答内容: // 问答 抽奖 开始 @server( group: questions ) service datacenter-api { @doc( summary: \"获取活动的信息\" ) @handler activitiesInfo get /questions/activities/info (Actid) returns (ActivityResp) @doc( summary: \"获取奖品信息\" ) @handler awardInfo get /questions/award/info (Actid) returns (ActivityResp) @handler awardList get /questions/award/list (Actid) returns (ActivityResp) } type AnswerReq struct { ActivityId int64 `json:\"actid\"` Answers string `json:\"answers\"` Score string `json:\"score\"` } type QuestionsAwardReq struct { ActivityId int64 `json:\"actid\"` AnswerId int64 `json:\"answerid\"` } type AnswerResp struct { Answers string `json:\"answers\"` Score string `json:\"score\"` } type AwardConvertReq struct { UserName string `json:\"username\"` Phone string `json:\"phone\"` LotteryId int64 `json:\"lotteryid\"` } @server( jwt: Auth group: questions middleware: Usercheck ) service datacenter-api { @doc( summary: \"获取题目\" ) @handler lists get /questions/lists (VoteReq) returns (AnswerResp) @doc( summary: \"提交答案\" ) @handler change post /questions/change (AnswerReq) returns (VoteResp) @doc( summary: \"获取分数\" ) @handler grade get /questions/grade (VoteReq) returns (VoteResp) @doc( summary: \"开始转盘\" ) @handler turntable post /questions/lottery/turntable (EnrollReq) returns (EnrollResp) @doc( summary: \"填写中奖信息人\" ) @handler lottery post /questions/lottery/convert (AwardConvertReq) returns (EnrollResp) } // 问答 抽奖 结束 search.api 搜索 type SearchReq struct { Keyword string `json:\"keyword\"` Page string `json:\"page\"` Size string `json:\"size\"` } type SearchResp struct { Data []ArticleReq `json:\"data\"` } type ArticleReq struct{ NewsId string `json:\"NewsId\"` NewsTitle string `json:\"NewsTitle\"` ImageUrl string `json:\"ImageUrl\"` } @server( group: search middleware: Admincheck ) service datacenter-api { @doc( summary: \"搜索\" ) @handler article get /search/article (SearchReq) returns (SearchResp) @handler articleInit get /search/articel/init (SearchReq) returns (SearchResp) @handler articleStore post /search/articel/store (ArticleReq) returns (ArticleReq) } 上面基本上写就写的API及文档的思路 生成datacenter api服务 ➜ datacenter goctl api go -api datacenter.api -dir . Done. ➜ datacenter treer . ├── datacenter.api ├── etc │ └── datacenter-api.yaml ├── go.mod ├── internal │ ├── config │ │ └── config.go │ ├── handler │ │ ├── common │ │ │ ├── appinfohandler.go │ │ │ ├── qiuniutokenhandler.go │ │ │ ├── snsinfohandler.go │ │ │ ├── votesverificationhandler.go │ │ │ └── wxtickethandler.go │ │ ├── routes.go │ │ ├── user │ │ │ ├── code2sessionhandler.go │ │ │ ├── loginhandler.go │ │ │ ├── pinghandler.go │ │ │ ├── registerhandler.go │ │ │ ├── userinfohandler.go │ │ │ └── wxloginhandler.go │ │ └── votes │ │ ├── activityicrviewhandler.go │ │ ├── activityinfohandler.go │ │ ├── enrollhandler.go │ │ ├── enrollinfohandler.go │ │ ├── enrolllistshandler.go │ │ └── votehandler.go │ ├── logic │ │ ├── common │ │ │ ├── appinfologic.go │ │ │ ├── qiuniutokenlogic.go │ │ │ ├── snsinfologic.go │ │ │ ├── votesverificationlogic.go │ │ │ └── wxticketlogic.go │ │ ├── user │ │ │ ├── code2sessionlogic.go │ │ │ ├── loginlogic.go │ │ │ ├── pinglogic.go │ │ │ ├── registerlogic.go │ │ │ ├── userinfologic.go │ │ │ └── wxloginlogic.go │ │ └── votes │ │ ├── activityicrviewlogic.go │ │ ├── activityinfologic.go │ │ ├── enrollinfologic.go │ │ ├── enrolllistslogic.go │ │ ├── enrolllogic.go │ │ └── votelogic.go │ ├── middleware │ │ └── usercheckmiddleware.go │ ├── svc │ │ └── servicecontext.go │ └── types │ └── types.go └── datacenter.go 14 directories, 43 files 我们打开 etc/datacenter-api.yaml 把必要的配置信息加上 Name: datacenter-api Log: Mode: console Host: 0.0.0.0 Port: 8857 Auth: AccessSecret: 你的jwtwon Secret AccessExpire: 86400 CacheRedis: - Host: 127.0.0.1:6379 Pass: 密码 Type: node UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc CommonRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc VotesRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: votes.rpc 上面的 UserRpc, CommonRpc ,还有 VotesRpc 这些我先写上,后面再来慢慢加。 我们先来写 CommonRpc 服务。 CommonRpc服务 新建项目目录 ➜ datacenter mkdir -p common/rpc && cd common/rpc 直接就新建在了,datacenter目录中,因为common 里面,可能以后会不只会提供rpc服务,可能还有api的服务,所以又加了rpc目录 goctl创建模板 ➜ rpc goctl rpc template -o=common.proto ➜ rpc ls common.proto 往里面填入内容: ➜ rpc cat common.proto syntax = \"proto3\"; option go_package = \"common\"; package common; message BaseAppReq{ int64 beid=1; } message BaseAppResp{ int64 beid=1; string logo=2; string sname=3; int64 isclose=4; string fullwebsite=5; } // 请求的api message AppConfigReq { int64 beid=1; int64 ptyid=2; } // 返回的值 message AppConfigResp { int64 id=1; int64 beid=2; int64 ptyid=3; string appid=4; string appsecret=5; string title=6; } service Common { rpc GetAppConfig(AppConfigReq) returns(AppConfigResp); rpc GetBaseApp(BaseAppReq) returns(BaseAppResp); } gotcl生成rpc服务 ➜ rpc goctl rpc proto -src common.proto -dir . protoc -I=/Users/jackluo/works/blogs/datacenter/common/rpc common.proto --go_out=plugins=grpc:/Users/jackluo/works/blogs/datacenter/common/rpc/common Done. ➜ rpc tree . ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 8 directories, 10 files 基本上,就把所有的目录规范和结构的东西都生成了,就不用纠结项目目录了,怎么放了,怎么组织了。 看一下,配置信息,里面可以写入mysql和其它redis的信息: Name: common.rpc ListenOn: 127.0.0.1:8081 Mysql: DataSource: root:admin@tcp(127.0.0.1:3306)/datacenter?charset=utf8&parseTime=true&loc=Asia%2FShanghai CacheRedis: - Host: 127.0.0.1:6379 Pass: Type: node Etcd: Hosts: - 127.0.0.1:2379 Key: common.rpc 我们再来加上数据库服务: ➜ rpc cd .. ➜ common ls rpc ➜ common pwd /Users/jackluo/works/blogs/datacenter/common ➜ common goctl model mysql datasource -url=\"root:admin@tcp(127.0.0.1:3306)/datacenter\" -table=\"base_app\" -dir ./model -c Done. ➜ common tree . ├── model │ ├── baseappmodel.go │ └── vars.go └── rpc ├── common │ └── common.pb.go ├── common.go ├── common.proto ├── commonclient │ └── common.go ├── etc │ └── common.yaml └── internal ├── config │ └── config.go ├── logic │ ├── getappconfiglogic.go │ └── getbaseapplogic.go ├── server │ └── commonserver.go └── svc └── servicecontext.go 10 directories, 12 files 这样基本的一个 rpc 就写完了,然后我们将rpc 和model 还有api串连起来,这个官方的文档已经很详细了,这里就只是贴一下代码: ➜ common cat rpc/internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { zrpc.RpcServerConf Mysql struct { DataSource string } CacheRedis cache.ClusterConf } 再在svc中修改: ➜ common cat rpc/internal/svc/servicecontext.go package svc import ( \"datacenter/common/model\" \"datacenter/common/rpc/internal/config\" \"github.com/zeromicro/go-zero/core/stores/sqlx\" ) type ServiceContext struct { c config.Config AppConfigModel model.AppConfigModel BaseAppModel model.BaseAppModel } func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.Mysql.DataSource) apm := model.NewAppConfigModel(conn, c.CacheRedis) bam := model.NewBaseAppModel(conn, c.CacheRedis) return &ServiceContext{ c: c, AppConfigModel: apm, BaseAppModel: bam, } } 上面的代码已经将 rpc 和 model 数据库关联起来了,我们现在再将 rpc 和 api 关联起来: ➜ datacenter cat internal/config/config.go package config import ( \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" ) type Config struct { rest.RestConf Auth struct { AccessSecret string AccessExpire int64 } UserRpc zrpc.RpcClientConf CommonRpc zrpc.RpcClientConf VotesRpc zrpc.RpcClientConf CacheRedis cache.ClusterConf } 加入 svc 服务中: ➜ datacenter cat internal/svc/servicecontext.go package svc import ( \"context\" \"datacenter/common/rpc/commonclient\" \"datacenter/internal/config\" \"datacenter/internal/middleware\" \"datacenter/shared\" \"datacenter/user/rpc/userclient\" \"datacenter/votes/rpc/votesclient\" \"fmt\" \"net/http\" \"time\" \"github.com/zeromicro/go-zero/core/logx\" \"github.com/zeromicro/go-zero/core/stores/cache\" \"github.com/zeromicro/go-zero/core/stores/redis\" \"github.com/zeromicro/go-zero/core/syncx\" \"github.com/zeromicro/go-zero/rest\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type ServiceContext struct { Config config.Config GreetMiddleware1 rest.Middleware GreetMiddleware2 rest.Middleware Usercheck rest.Middleware UserRpc userclient.User //用户 CommonRpc commonclient.Common VotesRpc votesclient.Votes Cache cache.Cache RedisConn *redis.Redis } func timeInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { stime := time.Now() err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { return err } fmt.Printf(\"调用 %s 方法 耗时: %v\\n\", method, time.Now().Sub(stime)) return nil } func NewServiceContext(c config.Config) *ServiceContext { ur := userclient.NewUser(zrpc.MustNewClient(c.UserRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) cr := commonclient.NewCommon(zrpc.MustNewClient(c.CommonRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) vr := votesclient.NewVotes(zrpc.MustNewClient(c.VotesRpc, zrpc.WithUnaryClientInterceptor(timeInterceptor))) //缓存 ca := cache.NewCache(c.CacheRedis, syncx.NewSharedCalls(), cache.NewCacheStat(\"dc\"), shared.ErrNotFound) rcon := redis.NewRedis(c.CacheRedis[0].Host, c.CacheRedis[0].Type, c.CacheRedis[0].Pass) return &ServiceContext{ Config: c, GreetMiddleware1: greetMiddleware1, GreetMiddleware2: greetMiddleware2, Usercheck: middleware.NewUserCheckMiddleware().Handle, UserRpc: ur, CommonRpc: cr, VotesRpc: vr, Cache: ca, RedisConn: rcon, } } 这样基本上,我们就可以在 logic 的文件目录中调用了: cat internal/logic/common/appinfologic.go package logic import ( \"context\" \"datacenter/internal/svc\" \"datacenter/internal/types\" \"datacenter/shared\" \"datacenter/common/model\" \"datacenter/common/rpc/common\" \"github.com/zeromicro/go-zero/core/logx\" ) type AppInfoLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewAppInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) AppInfoLogic { return AppInfoLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *AppInfoLogic) AppInfo(req types.Beid) (appconfig *common.BaseAppResp, err error) { //检查 缓存中是否有值 err = l.svcCtx.Cache.GetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) if err != nil && err == shared.ErrNotFound { appconfig, err = l.svcCtx.CommonRpc.GetBaseApp(l.ctx, &common.BaseAppReq{ Beid: req.Beid, }) if err != nil { return } err = l.svcCtx.Cache.SetCache(model.GetcacheBaseAppIdPrefix(req.Beid), appconfig) } return } 这样,基本就连接起来了,其它基本上就不用改了,UserRPC, VotesRPC 类似,这里就不在写了。 使用心得 go-zero 的确香,因为它有一个 goctl 的工具,他可以自动的把代码结构全部的生成好,我们就不再去纠结,目录结构 ,怎么组织,没有个好几年的架构能力是不好实现的,有什么规范那些,并发,熔断,完全不用,考虑其它的,专心的实现业务就好,像微服务,还要有服务发现,一系列的东西,都不用关心,因为 go-zero 内部已经实现了。 我写代码也写了有10多年了,之前一直用的 php,比较出名的就 laravel,thinkphp,基本上就是模块化的,像微服务那些实现真的有成本,但是你用上go-zero,你就像调api接口一样简单的开发,其它什么服务发现,那些根本就不用关注了,只需要关注业务。 一个好的语言,框架,他们的底层思维,永远都是效率高,不加班的思想,我相信go-zero会提高你和你团队或是公司的效率。go-zero的作者说,他们有个团队专门整理go-zero框架,目的也应该很明显,那就是提高,他们自己的开发效率,流程化,标准化,是提高工作效率的准则,像我们平时遇到了问题,或是遇到了bug,我第一个想到的不是怎么去解决我的bug,而是在想我的流程是不是有问题,我的哪个流程会导致bug,最后我相信 go-zero 能成为 微服务开发 的首选框架。 最后说说遇到的坑吧: grpc grpc 本人第一次用,然后就遇到了,有些字符为空时,字段值不显示的问题: 通过 grpc 官方库中的 jsonpb 来实现,官方在它的设定中有一个结构体用来实现 protoc buffer 转换为JSON结构,并可以根据字段来配置转换的要求。 跨域问题 go-zero 中设置了,感觉没有效果,大佬说通过nginx 设置,后面发现还是不行,最近强行弄到了一个域名下,后面有时间再解决。 sqlx go-zero 的 sqlx 问题,这个真的费了很长的时间: time.Time 这个数据结构,数据库中用的是 timestamp 这个 比如我的字段 是delete_at 默认数库设置的是null ,结果插入的时候,就报了 Incorrect datetime value: '0000-00-00' for column 'deleted_at' at row 1\"} 这个错,查询的时候报 deleted_at\\\": unsupported Scan, storing driver.Value type \\u003cnil\\u003e into type *time.Time\" 后面果断去掉了这个字段,字段上面加上 .omitempty 这个标签,好像也有用,db:\".omitempty\" 其次就是这个 Conversion from collation utf8_general_ci into utf8mb4_unicode_ci,这个导致的大概原因是,现在都喜欢用emj表情了,mysql数据识别不了。 数据连接 mysql 这边照样按照原始的方式,将配置文件修改编码格式,重新创建数据库,并且设置数据库编码为utf8mb4,排序规则为 utf8mb4_unicode_ci。 这样的话,所有的表还有string字段都是这个编码格式,如果不想所有的都是,可以单独设置,这个不是重点.因为在navicat上都好设置,手动点一下就行了。 重点来了:golang中使用的是 github.com/go-sql-driver/mysql 驱动,将连接 mysql的 dsn(因为我这使用的是gorm,所以dsn可能跟原生的格式不太一样,不过没关系, 只需要关注 charset 和 collation 就行了) root:password@/name?parseTime=True&loc=Local&charset=utf8 修改为: root:password@/name?parseTime=True&loc=Local&charset=utf8mb4&collation=utf8mb4_unicode_ci Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"stream.html":{"url":"stream.html","title":"流数据处理利器","keywords":"","body":"流数据处理利器 流处理 (Stream processing) 是一种计算机编程范式,其允许给定一个数据序列 (流处理数据源),一系列数据操作 (函数) 被应用到流中的每个元素。同时流处理工具可以显著提高程序员的开发效率,允许他们编写有效、干净和简洁的代码。 流数据处理在我们的日常工作中非常常见,举个例子,我们在业务开发中往往会记录许多业务日志,这些日志一般是先发送到 Kafka,然后再由 Job 消费 Kafaka 写到 elasticsearch,在进行日志流处理的过程中,往往还会对日志做一些处理,比如过滤无效的日志,做一些计算以及重新组合日志等等,示意图如下: 流处理工具fx go-zero 是一个功能完备的微服务框架,框架中内置了很多非常实用的工具,其中就包含流数据处理工具fx ,下面我们通过一个简单的例子来认识下该工具: package main import ( \"fmt\" \"os\" \"os/signal\" \"syscall\" \"time\" \"github.com/zeromicro/go-zero/core/fx\" ) func main() { ch := make(chan int) go inputStream(ch) go outputStream(ch) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) inputStream函数模拟了流数据的产生,outputStream函数模拟了流数据的处理过程,其中From函数为流的输入,Walk函数并发的作用在每一个item上,Filter函数对item进行过滤为true保留为false不保留,ForEach函数遍历输出每一个item元素。 流数据处理中间操作 一个流的数据处理可能存在许多的中间操作,每个中间操作都可以作用在流上。就像流水线上的工人一样,每个工人操作完零件后都会返回处理完成的新零件,同理流处理中间操作完成后也会返回一个新的流。 fx的流处理中间操作: 操作函数 功能 输入 Distinct 去除重复的item KeyFunc,返回需要去重的key Filter 过滤不满足条件的item FilterFunc,Option控制并发量 Group 对item进行分组 KeyFunc,以key进行分组 Head 取出前n个item,返回新stream int64保留数量 Map 对象转换 MapFunc,Option控制并发量 Merge 合并item到slice并生成新stream Reverse 反转item Sort 对item进行排序 LessFunc实现排序算法 Tail 与Head功能类似,取出后n个item组成新stream int64保留数量 Walk 作用在每个item上 WalkFunc,Option控制并发量 下图展示了每个步骤和每个步骤的结果: 用法与原理分析 From 通过From函数构建流并返回Stream,流数据通过channel进行存储: // 例子 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Filter Filter函数提供过滤item的功能,FilterFunc定义过滤逻辑true保留item,false则不保留: // 例子 保留偶数 s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} fx.From(func(source chan Group Group对流数据进行分组,需定义分组的key,数据分组后以slice存入channel: // 例子 按照首字符\"g\"或者\"p\"分组,没有则分到另一组 ss := []string{\"golang\", \"google\", \"php\", \"python\", \"java\", \"c++\"} fx.From(func(source chan Reverse reverse可以对流中元素进行反转处理: // 例子 fx.Just(1, 2, 3, 4, 5).Reverse().ForEach(func(item interface{}) { fmt.Println(item) }) // 源码 func (p Stream) Reverse() Stream { var items []interface{} // 获取流中数据 for item := range p.source { items = append(items, item) } // 反转算法 for i := len(items)/2 - 1; i >= 0; i-- { opp := len(items) - 1 - i items[i], items[opp] = items[opp], items[i] } // 写入流 return Just(items...) } Distinct distinct对流中元素进行去重,去重在业务开发中比较常用,经常需要对用户id等做去重操作: // 例子 fx.Just(1, 2, 2, 2, 3, 3, 4, 5, 6).Distinct(func(item interface{}) interface{} { return item }).ForEach(func(item interface{}) { fmt.Println(item) }) // 结果为 1,2,3,4,5,6 // 源码 func (p Stream) Distinct(fn KeyFunc) Stream { source := make(chan interface{}) threading.GoSafe(func() { defer close(source) // 通过key进行去重,相同key只保留一个 keys := make(map[interface{}]lang.PlaceholderType) for item := range p.source { key := fn(item) // key存在则不保留 if _, ok := keys[key]; !ok { source Walk Walk函数并发的作用在流中每一个item上,可以通过WithWorkers设置并发数,默认并发数为16,最小并发数为1,如设置unlimitedWorkers为true则并发数无限制,但并发写入流中的数据由defaultWorkers限制,WalkFunc中用户可以自定义后续写入流中的元素,可以不写入也可以写入多个元素: // 例子 fx.Just(\"aaa\", \"bbb\", \"ccc\").Walk(func(item interface{}, pipe chan 并发处理 fx工具除了进行流数据处理以外还提供了函数并发功能,在微服务中实现某个功能往往需要依赖多个服务,并发的处理依赖可以有效的降低依赖耗时,提升服务的性能。 fx.Parallel(func() { userRPC() // 依赖1 }, func() { accountRPC() // 依赖2 }, func() { orderRPC() // 依赖3 }) 注意fx.Parallel进行依赖并行处理的时候不会有error返回,如需有error返回或者有一个依赖报错需要立马结束依赖请求请使用MapReduce 工具进行处理。 总结 本篇文章介绍了流处理的基本概念和go-zero中的流处理工具fx,在实际的生产中流处理场景应用也非常多,希望本篇文章能给大家带来一定的启发,更好的应对工作中的流处理场景。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"online-exchange.html":{"url":"online-exchange.html","title":"10月3日线上交流问题汇总","keywords":"","body":"10月3日线上交流问题汇总 go-zero适用场景 希望说说应用场景,各个场景下的优势 高并发的微服务系统 支撑千万级日活,百万级QPS 完整的微服务治理能力 支持自定义中间件 很好的管理了数据库和缓存 有效隔离故障 低并发的单体系统 这种系统直接使用api层即可,无需rpc服务 各个功能的使用场景以及使用案例 限流 熔断 降载 超时 可观测性 go-zero的实际体验 服务很稳 前后端接口一致性,一个api文件即可生成前后端代码 规范、代码量少,意味着bug少 免除api文档,极大降低沟通成本 代码结构完全一致,便于维护和接手 微服务的项目结构, monorepo的 CICD 处理 bookstore ├── api │ ├── etc │ └── internal │ ├── config │ ├── handler │ ├── logic │ ├── svc │ └── types └── rpc ├── add │ ├── adder │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb ├── check │ ├── checker │ ├── etc │ ├── internal │ │ ├── config │ │ ├── logic │ │ ├── server │ │ └── svc │ └── pb └── model mono repo的CI我们是通过gitlab做的,CD使用jenkins CI尽可能更严格的模式,比如-race,使用sonar等工具 CD有开发、测试、预发、灰度和正式集群 晚6点上灰度、无故障的话第二天10点自动同步到正式集群 正式集群分为多个k8s集群,有效的防止单集群故障,直接摘除即可,集群升级更有好 如何部署,如何监控? 全量K8S,通过jenkins自动打包成docker镜像,按照时间打包tag,这样可以一眼看出哪一天的镜像 上面已经讲了,预发->灰度->正式 Prometheus+自建dashboard服务 基于日志检测服务和请求异常 如果打算换go-zero框架重构业务,如何做好线上业务稳定安全用户无感切换?另外咨询下如何进行服务划分? 逐步替换,从外到内,加个proxy来校对,校对一周后可以切换 如有数据库重构,则需要做好新老同步 服务划分按照业务来,遵循从粗到细的原则,避免一个api一个微服务 数据拆分对于微服务来讲尤为重要,上层好拆,数据难拆,尽可能保证按照业务拆分数据 服务发现 服务发现 etcd 的 key 的设计 服务key+时间戳,服务进程数存在时间戳冲突的概率极低,忽略 etcd服务发现与治理, 异常捕获与处理异常 为啥k8s还使用etcd做服务发现,因为dns的刷新有延迟,导致滚动更新会有大量失败,而etcd可以做到完全无损更新 etcd集群直接部署在k8s集群内,因为多个正式集群,集群单点和注册避免混乱 针对etcd异常或者leader切换,自动侦测并刷新,当etcd有异常不能恢复时,不会刷新服务列表,保障服务依然可用 缓存的设计与使用案例 分布式多redis集群,线上最大几十个集群为同一个服务提供缓存服务 无缝扩缩容 不存在没有过期时间的缓存,避免大量不常使用的数据占用资源,默认一周 缓存穿透,没有的数据会短暂缓存一分钟,避免刷接口或大量不存在的数据请求带垮系统 缓存击穿,一个进程只会刷新一次同一个数据,避免热点数据被大量同时加载 缓存雪崩,对缓存过期时间自动做了jitter,5%的标准变差,使得一周的过期时间分布在16小时内,有效防止了雪崩 我们线上数据库都有缓存,否则无法支撑海量并发 自动缓存管理已经内置于go-zero,并可以通过goctl自动生成代码 能否讲解下, 中间件,拦截器的设计思想 洋葱模型 本中间件处理,比如限流,熔断等,然后决定是否调用next next调用 对next调用返回结果做处理 微服务的事务处理怎么实现好,gozero分布式事务设计和实现,有什么好中间件推荐 2PC,两阶段提交 TCC,Try-Confirm-Cancel 消息队列,最大尝试 人工补偿 多级 goroutine 的异常捕获 ,怎么设计比较好 微服务系统请求异常应该隔离,不能让单个异常请求带崩整个进程 go-zero自带了RunSafe/GoSafe,用来防止单个异常请求导致进程崩溃 监控需要跟上,防止异常过量而不自知 fail fast和故障隔离的矛盾点 k8s配置的生成与使用(gateway, service, slb) 内部自动生成k8s的yaml文件,过于依赖配置而未开源 打算在bookstore的示例里加上k8s配置样板 slb->nginx->nodeport->api gateway->rpc service gateway限流、熔断和降载 限流分为两种:并发控制和分布式限流 并发控制用来防止瞬间过量请求,保护系统不被打垮 分布式限流用来给不同服务配置不同的quota 熔断是为了对依赖的服务进行保护,当一个服务出现大量异常的时候,调用者应该给予保护,使其有机会恢复正常,同时也达到fail fast的效果 降载是为了保护当前进程资源耗尽而陷入彻底不可用,确保尽可能服务好能承载的最大请求量 降载配合k8s,可以有效保护k8s扩容,k8s扩容分钟级,go-zero降载秒级 介绍core中好用的组件,如timingwheel等,讲讲设计思路 布隆过滤器 进程内cache RollingWindow TimingWheel 各种executors fx包,map/reduce/filter/sort/group/distinct/head/tail... 一致性hash实现 分布式限流实现 mapreduce,带cancel能力 syncx包里有大量的并发工具 如何快速增加一种rpc协议支持,將跨机发现改为调本机节点,并关闭复杂filter和负载均衡功能 go-zero跟grpc关系还是比较紧密的,设计之初没有考虑支持grpc以外的协议 如果要增加的话,那就只能fork出来魔改了 调本机直接用direct的scheme即可 为啥要去掉filter和负载均衡?如果要去的话,fork了改,但没必要 日志和监控和链路追踪的设计和实现思路,最好有大概图解 日志和监控我们使用prometheus, 自定义dashboard服务,捆绑提交数据(每分钟) 链路追踪可以看出调用关系,自动记录trace日志 go-zero框架有用到什么池化技术吗?如果有,在哪些core代码里面可以参考 一般不需要提前优化,过度优化是大忌 core/syncx/pool.go里面定义了带过期时间的通用池化技术 go-zero用到了那些性能测试方法框架,有代码参考吗?可以说说思路和经验 go benchmark 压测可以通过现有业务日志样本,来按照预估等比放大 压测一定要压到系统扛不住,看第一个瓶颈在哪里,改完再压,循环 说一下代码的抽象经验和心得 Don’t repeat yourself 你未必需要它,之前经常有业务开发人员问我可不可以增加这个功能或那个功能,我一般都会仔细询问深层次目的,很多时候会发现其实这个功能是多余的,不需要才是最佳实践 Martin Fowler提出出现三次再抽象的原则,有时有些同事会找我往框架里增加一个功能,我思考后经常会回答这个你先在业务层写,其它地方也有需要了你再告诉我,三次出现我会考虑集成到框架里 一个文件应该尽量只做一件事,每个文件尽可能控制在200行以内,一个函数尽可能控制在50行以内,这样不需要滚动就可以看到整个函数 需要抽象和提炼的能力,多去思考,经常回头思考之前的架构或实现 你会就go-zero 框架从设计到实践出书吗?框架以后的发展规划是什么? 暂无出书计划,做好框架是最重要的 继续着眼于工程效率 提升服务治理能力 帮助业务开发尽可能快速落地 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"contributor.html":{"url":"contributor.html","title":"贡献人员","keywords":"","body":"社区贡献 作者 kevwan go-zero参与人员 kevwan dependabot[bot] kesonan kingxt chenquan MarkJoyMa zcong1993 fynxiu testwill zhoushuguang szpnygo miaogaolin dfang bittoy Suyghur heyanfu sjatsh Mikaelemmmm foliet wubenqi fondoger taobig shenbaise9527 reatang Code-Fight xiaowei520 wsx864321 chowyu12 chensylz phibe2017 anyoptional LeeDF POABOB czyt zjbztianya kurimi1 pig-peppa re-dylan fanlongteng knight0zh xiaoyuzdy veezhang jiang4869 mlr3000 mywaystay ronething-bot sado0823 supermario1990 lizhichao voidint cjf8134 smithyj showurl zzzfwww HarryWang29 workman-Lu wuqinqiang soasurs weicut almas1992 AlexLast appleboy pipi-lv Howie59 ShyunnY SnakeHacker sohamtembhurne toby1991 masonchen2014 lucaq lchjczw heyehang guonaihong wangzeping722 fyyang kscooo cuishuang bensonfx lovelly lowang-bh magickeha yangwenmai me-cs mlboy mongobaba moyrne ahmczsy lvillis imzhongqi r00mz mamil safeoy skyoct sniperwzq SpectatorNan gq-tang yiGmMk fffreedom linyihai fisnone foyon genewoo gongluck hanxuanliang tfzxyinhao lhcGinv hexiaoen iyyzh Janetyu demoManito jichangyun Kangkeizai kevin0527 byops kunyu liumin-go lord63 liyiwu zeromake zzhaolei zzZZzzz888 qwxingzhe sixwaaaay liuqing6767 peasfarmer lingwei0604 linganmin citizen233 u2nyakim wenj91 congim 600ML seth-shi AaronCXZ lppgo wanghaha-dev HappyUncle RivenChan toventang vankillua jsonMark shssen rcyw weibobo windk wojiukankan SeigeC wuleiming2009 wwek wxc421 xiang-xx xt-inking TonoT xybingbing yangjinheng yangkequn runtu666 yedf2 2822132073 l306287405 nianiaJR richardJiang joshq00 Julian-Chu 0xkookoo wanjunfeng Kimjin-gd 0XFF-96 WangLeonard letian-jiang fzdwx liamhao mervin0502 JasonMing vfmh notrynosuccess ofey404 oraoto ivalue2333 7134g lqlspace alonexy amorist 0Armaan025 tvermaashutosh AtlanCI Awadabang BYT0723 bhargavshirin changkun chrislee87 CrazyZard defp EinfachePhy qiujiafei gokure Hkesd eltociear RyanTokManMokMTM brickzzhang zlx362211854 a0v0 xiongqq345 aimuz Ouyangan anstns benyingY bigrocs jiangbohhh accaolei x1nchen mycatone charliecen ch3nnn cuisongliu dahaihu dylanNew edieruby elza2 codeErrorSleep WqyJh reneleonhardt qwernser ren544735689 Jancd SgtDaJim SleeplessBot 1067088037 suravshresth zhouyusd cgx027 wangyi12358 tim1116 tonywangcn cch123 ChengXavier cubxxw lxy1992 fulldog 文档贡献人员 kevwan loocor koulerz citizen233 Mikaelemmmm avtion helloshaohua wuyang910217 wuqinqiang zcong1993 jackluo2012 zoulux karnin keehao linganmin ronething-bot topfanfan belm ice-waves hwb2017 hbinr ha-ni-cc gggwvg chensylz auula Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:51 "},"doc-contibute.html":{"url":"doc-contibute.html","title":"文档贡献","keywords":"","body":"文档贡献 怎么贡献文档? 点击顶部\"编辑此页\"按钮即可进入源码仓库对应的文件,开发人员将修改(添加)的文档通过pr形式提交, 我们收到pr后会进行文档审核,一旦审核通过即可更新文档。 可以贡献哪些文档? 文档编写错误 文档不规范、不完整 go-zero应用实践、心得 组件中心 文档pr通过后文档多久会更新? 在pr接受后,github action会自动build gitbook并发布,因此在github action成功后1-2分钟即可查看更新后的文档。 文档贡献注意事项 纠错、完善源文档可以直接编写原来的md文件 新增组件文档需要保证文档排版、易读,且组件文档需要放在组件中心子目录中 go-zero应用实践分享可以直接放在开发实践子目录下 目录结构规范 目录结构不宜过深,最好不要超过3层 组件文档需要在归属到组件中心,如* [开发实践](practise.md) * [logx](logx.md) * [bloom](bloom.md) * [executors](executors.md) * 你的文档目录名称 应用实践需要归属到开发实践,如* [开发实践](practise.md) * [我是如何用go-zero 实现一个中台系统](datacenter.md) * [流数据处理利器](stream.md) * [10月3日线上交流问题汇总](online-exchange.md * 你的文档目录名称 开发实践文档模板 # 标题 > 作者:填入作者名称 > > 原文连接: 原文连接 some markdown content 猜你想看 怎么参与贡献 Github Pull request Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"error.html":{"url":"error.html","title":"常见错误处理","keywords":"","body":"常见错误处理 Windows上报错 A required privilege is not held by the client. 解决方法:\"以管理员身份运行\" goctl 即可。 grpc引起错误 错误一 protoc-gen-go: unable to determine Go import path for \"greet.proto\" Please specify either: • a \"go_package\" option in the .proto source file, or • a \"M\" argument on the command line. See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information. --go_out: protoc-gen-go: Plugin failed with status code 1. 解决方法: go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 protoc-gen-go安装失败 go get github.com/golang/protobuf/protoc-gen-go: module github.com/golang/protobuf/protoc-gen-go: Get \"https://proxy.golang.org/github.com/golang/protobuf/protoc-gen-go/@v/list\": dial tcp 216.58.200.49:443: i/o timeout 请确认GOPROXY已经设置,GOPROXY设置见go module配置 api服务启动失败 error: config file etc/user-api.yaml, error: type mismatch for field xx 请确认user-api.yaml配置文件中配置项是否已经配置,如果有值,检查一下yaml配置文件是否符合yaml格式。 goctl找不到 command not found: goctl 请确保goctl已经安装或者goctl是否已经添加到环境变量 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"source.html":{"url":"source.html","title":"相关源码","keywords":"","body":"相关源码 demo源码 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"tips.html":{"url":"tips.html","title":"阅读须知","keywords":"","body":"阅读须知 本文档从快速入门,详细项目开发流程,go-zero服务设计思想,goctl工具的使用等维度进行了介绍, 对于刚刚接触go或go-zero的同学需要把这些篇幅都看完才能有所了解,因此有些费力,这里建议大家阅读的方法。 保持耐心跟着文档目录进行,文档是按照从简单到深入的渐进式过程编写的。 在遇到问题或错误时,请一定记住多查FAQ。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"shorturl.html":{"url":"shorturl.html","title":"快速构建高并发微服务","keywords":"","body":"快速构建高并发微服务 English | 简体中文 0. 为什么说做好微服务很难 要想做好微服务,我们需要理解和掌握的知识点非常多,从几个维度上来说: 基本功能层面 并发控制 & 限流,避免服务被突发流量击垮 服务注册与服务发现,确保能够动态侦测增减的节点 负载均衡,需要根据节点承受能力分发流量 超时控制,避免对已超时请求做无用功 熔断设计,快速失败,保障故障节点的恢复能力 高阶功能层面 请求认证,确保每个用户只能访问自己的数据 链路追踪,用于理解整个系统和快速定位特定请求的问题 日志,用于数据收集和问题定位 可观测性,没有度量就没有优化 对于其中每一点,我们都需要用很长的篇幅来讲述其原理和实现,那么对我们后端开发者来说,要想把这些知识点都掌握并落实到业务系统里,难度是非常大的,不过我们可以依赖已经被大流量验证过的框架体系。go-zero 微服务框架就是为此而生。 另外,我们始终秉承 工具大于约定和文档 的理念。我们希望尽可能减少开发人员的心智负担,把精力都投入到产生业务价值的代码上,减少重复代码的编写,所以我们开发了 goctl 工具。 下面我通过短链微服务来演示通过 go-zero 快速的创建微服务的流程,走完一遍,你就会发现:原来编写微服务如此简单! 1. 什么是短链服务 短链服务就是将长的 URL 网址,通过程序计算等方式,转换为简短的网址字符串。 写此短链服务是为了从整体上演示 go-zero 构建完整微服务的过程,算法和实现细节尽可能简化了,所以这不是一个高阶的短链服务。 2. 短链微服务架构图 这里只用了 Transform RPC 一个微服务,并不是说 API Gateway 只能调用一个微服务,只是为了最简演示 API Gateway 如何调用 RPC 微服务而已 在真正项目里要尽可能每个微服务使用自己的数据库,数据边界要清晰 3. goctl 各层代码生成一览 所有绿色背景的功能模块是自动生成的,按需激活,红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑,各层示意图分别如下: API Gateway RPC model 下面我们来一起完整走一遍快速构建微服务的流程,Let’s Go!🏃‍♂️ 4. 准备工作 安装 etcd, mysql, redis 安装 protoc-gen-go $ go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2 安装 protoc $ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.14.0/protoc-3.14.0-linux-x86_64.zip $ unzip protoc-3.14.0-linux-x86_64.zip $ mv bin/protoc /usr/local/bin/ 安装 goctl 工具 $ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/go-zero/tools/goctl 创建工作目录 shorturl 和 shorturl/api mkdir -p shorturl/api 在 shorturl 目录下执行 go mod init shorturl 初始化 go.mod module shorturl go 1.15 require ( github.com/golang/mock v1.4.3 github.com/golang/protobuf v1.4.2 github.com/zeromicro/go-zero v1.3.0 golang.org/x/net v0.0.0-20200707034311-ab3426394381 google.golang.org/grpc v1.29.1 ) 注意:这里可能存在 grpc 版本依赖的问题,可以用以上配置 5. 编写 API Gateway 代码 在 shorturl/api 目录下通过 goctl 生成 api/shorturl.api: $ goctl api -o shorturl.api 编辑 api/shorturl.api,为了简洁,去除了文件开头的 info,代码如下: type ( expandReq { shorten string `form:\"shorten\"` } expandResp { url string `json:\"url\"` } ) type ( shortenReq { url string `form:\"url\"` } shortenResp { shorten string `json:\"shorten\"` } ) service shorturl-api { @server( handler: ShortenHandler ) get /shorten(shortenReq) returns(shortenResp) @server( handler: ExpandHandler ) get /expand(expandReq) returns(expandResp) } type 用法和 go 一致,service 用来定义 get/post/head/delete 等 api 请求,解释如下: service shorturl-api { 这一行定义了 service 名字 @server 部分用来定义 server 端用到的属性 handler 定义了服务端 handler 名字 get /shorten(shortenReq) returns(shortenResp) 定义了 get 方法的路由、请求参数、返回参数等 使用 goctl 生成 API Gateway 代码 $ goctl api go -api shorturl.api -dir . 生成的文件结构如下: . ├── api │ ├── etc │ │ └── shorturl-api.yaml // 配置文件 │ ├── internal │ │ ├── config │ │ │ └── config.go // 定义配置 │ │ ├── handler │ │ │ ├── expandhandler.go // 实现 expandHandler │ │ │ ├── routes.go // 定义路由处理 │ │ │ └── shortenhandler.go // 实现 shortenHandler │ │ ├── logic │ │ │ ├── expandlogic.go // 实现 ExpandLogic │ │ │ └── shortenlogic.go // 实现 ShortenLogic │ │ ├── svc │ │ │ └── servicecontext.go // 定义 ServiceContext │ │ └── types │ │ └── types.go // 定义请求、返回结构体 │ ├── shorturl.api │ └── shorturl.go // main 入口定义 ├── go.mod └── go.sum 启动 API Gateway 服务,默认侦听在 8888 端口 $ go run shorturl.go -f etc/shorturl-api.yaml 测试 API Gateway 服务 $ curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 27 Aug 2020 14:31:39 GMT Content-Length: 15 {\"shorten\":\"\"} 可以看到我们 API Gateway 其实啥也没干,就返回了个空值,接下来我们会在 rpc 服务里实现业务逻辑 可以修改 internal/svc/servicecontext.go 来传递服务依赖(如果需要) 实现逻辑可以修改 internal/logic 下的对应文件 可以通过 goctl 生成各种客户端语言的 api 调用代码 到这里,你已经可以通过 goctl 生成客户端代码给客户端同学并行开发了,支持多种语言,详见文档 6. 编写 transform rpc 服务 在 shorturl 目录下创建 rpc 目录 在 rpc/transform 目录下编写 transform.proto 文件 可以通过命令生成 proto 文件模板 $ goctl rpc template -o transform.proto 修改后文件内容如下: syntax = \"proto3\"; package transform; message expandReq { string shorten = 1; } message expandResp { string url = 1; } message shortenReq { string url = 1; } message shortenResp { string shorten = 1; } service transformer { rpc expand(expandReq) returns(expandResp); rpc shorten(shortenReq) returns(shortenResp); } 用 goctl 生成 rpc 代码,在 rpc/transform 目录下执行命令 $ goctl rpc proto -src transform.proto -dir . 注意:不能在 GOPATH 目录下执行以上命令 文件结构如下: rpc/transform ├── etc │ └── transform.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ ├── expandlogic.go // expand 业务逻辑在这里实现 │ │ └── shortenlogic.go // shorten 业务逻辑在这里实现 │ ├── server │ │ └── transformerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义 ServiceContext,传递依赖 ├── pb │ └── transform.pb.go ├── transform.go // rpc 服务 main 函数 ├── transform.proto └── transformer ├── transformer.go // 提供了外部调用方法,无需修改 ├── transformer_mock.go // mock 方法,测试用 └── types.go // request/response 结构体定义 直接可以运行,如下: $ go run transform.go -f etc/transform.yaml Starting rpc server at 127.0.0.1:8080... 查看服务是否注册 $ ETCDCTL_API=3 etcdctl get transform.rpc --prefix transform.rpc/7587851893787585061 127.0.0.1:8080 etc/transform.yaml 文件里可以修改侦听端口等配置 7. 修改 API Gateway 代码调用 transform rpc 服务 修改配置文件 shorturl-api.yaml,增加如下内容 Transform: Etcd: Hosts: - localhost:2379 Key: transform.rpc 通过 etcd 自动去发现可用的 transform 服务 修改 internal/config/config.go 如下,增加 transform 服务依赖 type Config struct { rest.RestConf Transform zrpc.RpcClientConf // 手动代码 } 修改 internal/svc/servicecontext.go,如下: type ServiceContext struct { Config config.Config Transformer transformer.Transformer // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Transformer: transformer.NewTransformer(zrpc.MustNewClient(c.Transform)), // 手动代码 } } 通过 ServiceContext 在不同业务逻辑之间传递依赖 修改 internal/logic/expandlogic.go 里的 Expand 方法,如下: func (l *ExpandLogic) Expand(req types.ExpandReq) (types.ExpandResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Expand(l.ctx, &transformer.ExpandReq{ Shorten: req.Shorten, }) if err != nil { return types.ExpandResp{}, err } return types.ExpandResp{ Url: resp.Url, }, nil // 手动代码结束 } 通过调用 transformer 的 Expand 方法实现短链恢复到 url 修改 internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(req types.ShortenReq) (types.ShortenResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Shorten(l.ctx, &transformer.ShortenReq{ Url: req.Url, }) if err != nil { return types.ShortenResp{}, err } return types.ShortenResp{ Shorten: resp.Shorten, }, nil // 手动代码结束 } 有的版本生成返回值可能是指针类型,需要自己调整下 通过调用 transformer 的 Shorten 方法实现 url 到短链的变换 至此,API Gateway 修改完成,虽然贴的代码多,但是其中修改的是很少的一部分,为了方便理解上下文,我贴了完整代码,接下来处理 CRUD+cache 8. 定义数据库表结构,并生成 CRUD+cache 代码 shorturl 下创建 rpc/transform/model 目录:mkdir -p rpc/transform/model 在 rpc/transform/model 目录下编写创建 shorturl 表的 sql 文件 shorturl.sql,如下: CREATE TABLE `shorturl` ( `shorten` varchar(255) NOT NULL COMMENT 'shorten key', `url` varchar(255) NOT NULL COMMENT 'original url', PRIMARY KEY(`shorten`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 创建 DB 和 table create database gozero; source shorturl.sql; 在 rpc/transform/model 目录下执行如下命令生成 CRUD+cache 代码,-c 表示使用 redis cache $ goctl model mysql ddl -c -src shorturl.sql -dir . 也可以用 datasource 命令代替 ddl 来指定数据库链接直接从 schema 生成 生成后的文件结构如下: Plain Text rpc/transform/model ├── shorturl.sql ├── shorturlmodel.go // CRUD+cache 代码 └── vars.go // 定义常量和变量 9. 修改 shorten/expand rpc 代码调用 crud+cache 代码 修改 rpc/transform/etc/transform.yaml,增加如下内容: DataSource: root:password@tcp(localhost:3306)/gozero Table: shorturl Cache: - Host: localhost:6379 可以使用多个 redis 作为 cache,支持 redis 单点或者 redis 集群 修改 rpc/transform/internal/config/config.go,如下: type Config struct { zrpc.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 } 增加了 mysql 和 redis cache 配置 修改 rpc/transform/internal/svc/servicecontext.go,如下: type ServiceContext struct { c config.Config Model model.ShorturlModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache), // 手动代码 } } 修改 rpc/transform/internal/logic/expandlogic.go,如下: func (l *ExpandLogic) Expand(in *transform.ExpandReq) (*transform.ExpandResp, error) { // 手动代码开始 res, err := l.svcCtx.Model.FindOne(l.ctx, in.Shorten) if err != nil { return nil, err } return &transform.ExpandResp{ Url: res.Url, }, nil // 手动代码结束 } 修改 rpc/transform/internal/logic/shortenlogic.go,如下: func (l *ShortenLogic) Shorten(in *transform.ShortenReq) (*transform.ShortenResp, error) { // 手动代码开始,生成短链接 key := hash.Md5Hex([]byte(in.Url))[:6] object, _ := l.svcCtx.Model.FindOne(l.ctx, key) if object != nil { return &transform.ShortenResp{ Shorten: key, }, nil } _, err := l.svcCtx.Model.Insert(l.ctx, &model.Shorturl{ Shorten: key, Url: in.Url, }) if err != nil { return nil, err } return &transform.ShortenResp{ Shorten: key, }, nil // 手动代码结束 } 至此代码修改完成,凡是手动修改的代码我加了标注 注意: undefined cache,你需要 import \"github.com/zeromicro/go-zero/core/stores/cache\" undefined model, sqlx, hash 等,你需要在文件中 import \"shorturl/rpc/transform/model\" import \"github.com/zeromicro/go-zero/core/stores/sqlx\" 10. 完整调用演示 shorten api 调用 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:49:49 GMT Content-Length: 21 {\"shorten\":\"f35b2a\"} expand api 调用 $ curl -i \"http://localhost:8888/expand?shorten=f35b2a\" 返回如下: HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:51:53 GMT Content-Length: 34 {\"url\":\"http://www.xiaoheiban.cn\"} 11. Benchmark 因为写入依赖于 mysql 的写入速度,就相当于压 mysql 了,所以压测只测试了 expand 接口,相当于从 mysql 里读取并利用缓存,shorten.lua 里随机从 db 里获取了 100 个热 key 来生成压测请求 可以看出在我的 MacBook Pro 上能达到 3 万 + 的 qps。 12. 完整代码 https://github.com/zeromicro/zero-examples/tree/main/shorturl 12. 总结 我们一直强调 工具大于约定和文档。 go-zero 不只是一个框架,更是一个建立在框架 + 工具基础上的,简化和规范了整个微服务构建的技术体系。 我们在保持简单的同时也尽可能把微服务治理的复杂度封装到了框架内部,极大的降低了开发人员的心智负担,使得业务开发得以快速推进。 通过 go-zero+goctl 生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。 有任何好的提升工程效率的想法,随时欢迎交流!👏 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"breaker-algorithms.html":{"url":"breaker-algorithms.html","title":"熔断原理与实现","keywords":"","body":"熔断原理与实现 在微服务中服务间依赖非常常见,比如评论服务依赖审核服务而审核服务又依赖反垃圾服务,当评论服务调用审核服务时,审核服务又调用反垃圾服务,而这时反垃圾服务超时了,由于审核服务依赖反垃圾服务,反垃圾服务超时导致审核服务逻辑一直等待,而这个时候评论服务又在一直调用审核服务,审核服务就有可能因为堆积了大量请求而导致服务宕机 由此可见,在整个调用链中,中间的某一个环节出现异常就会引起上游调用服务出现一些列的问题,甚至导致整个调用链的服务都宕机,这是非常可怕的。因此一个服务作为调用方调用另一个服务时,为了防止被调用服务出现问题进而导致调用服务出现问题,所以调用服务需要进行自我保护,而保护的常用手段就是熔断 熔断器原理 熔断机制其实是参考了我们日常生活中的保险丝的保护机制,当电路超负荷运行时,保险丝会自动的断开,从而保证电路中的电器不受损害。而服务治理中的熔断机制,指的是在发起服务调用的时候,如果被调用方返回的错误率超过一定的阈值,那么后续的请求将不会真正发起请求,而是在调用方直接返回错误 在这种模式下,服务调用方为每一个调用服务(调用路径)维护一个状态机,在这个状态机中有三个状态: 关闭(Closed):在这种状态下,我们需要一个计数器来记录调用失败的次数和总的请求次数,如果在某个时间窗口内,失败的失败率达到预设的阈值,则切换到断开状态,此时开启一个超时时间,当到达该时间则切换到半关闭状态,该超时时间是给了系统一次机会来修正导致调用失败的错误,以回到正常的工作状态。在关闭状态下,调用错误是基于时间的,在特定的时间间隔内会重置,这能够防止偶然错误导致熔断器进去断开状态 打开(Open):在该状态下,发起请求时会立即返回错误,一般会启动一个超时计时器,当计时器超时后,状态切换到半打开状态,也可以设置一个定时器,定期的探测服务是否恢复 半打开(Half-Open):在该状态下,允许应用程序一定数量的请求发往被调用服务,如果这些调用正常,那么可以认为被调用服务已经恢复正常,此时熔断器切换到关闭状态,同时需要重置计数。如果这部分仍有调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到打开状态,然后重置计数器,半打开状态能够有效防止正在恢复中的服务被突然大量请求再次打垮 服务治理中引入熔断机制,使得系统更加稳定和有弹性,在系统从错误中恢复的时候提供稳定性,并且减少了错误对系统性能的影响,可以快速拒绝可能导致错误的服务调用,而不需要等待真正的错误返回 熔断器引入 上面介绍了熔断器的原理,在了解完原理后,你是否有思考我们如何引入熔断器呢?一种方案是在业务逻辑中可以加入熔断器,但显然是不够优雅也不够通用的,因此我们需要把熔断器集成在框架内,在zRPC框架内就内置了熔断器 我们知道,熔断器主要是用来保护调用端,调用端在发起请求的时候需要先经过熔断器,而客户端拦截器正好兼具了这个这个功能,所以在zRPC框架内熔断器是实现在客户端拦截器内,拦截器的原理如下图: 对应的代码为: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // 基于请求方法进行熔断 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正发起调用 return invoker(ctx, method, req, reply, cc, opts...) // codes.Acceptable判断哪种错误需要加入熔断错误计数 }, codes.Acceptable) } 熔断器实现 zRPC中熔断器的实现参考了Google Sre过载保护算法,该算法的原理如下: 请求数量(requests):调用方发起请求的数量总和 请求接受数量(accepts):被调用方正常处理的请求数量 在正常情况下,这两个值是相等的,随着被调用方服务出现异常开始拒绝请求,请求接受数量(accepts)的值开始逐渐小于请求数量(requests),这个时候调用方可以继续发送请求,直到requests = K * accepts,一旦超过这个限制,熔断器就回打开,新的请求会在本地以一定的概率被抛弃直接返回错误,概率的计算公式如下: 通过修改算法中的K(倍值),可以调节熔断器的敏感度,当降低该倍值会使自适应熔断算法更敏感,当增加该倍值会使得自适应熔断算法降低敏感度,举例来说,假设将调用方的请求上限从 requests = 2 acceptst 调整为 requests = 1.1 accepts 那么就意味着调用方每十个请求之中就有一个请求会触发熔断 代码路径为go-zero/core/breaker type googleBreaker struct { k float64 // 倍值 默认1.5 stat *collection.RollingWindow // 滑动时间窗口,用来对请求失败和成功计数 proba *mathx.Proba // 动态概率 } 自适应熔断算法实现 func (b *googleBreaker) accept() error { accepts, total := b.history() // 请求接受数量和请求总量 weightedAccepts := b.k * float64(accepts) // 计算丢弃请求概率 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio 每次发起请求会调用doReq方法,在这个方法中首先通过accept效验是否触发熔断,acceptable用来判断哪些error会计入失败计数,定义如下: func Acceptable(err error) bool { switch status.Code(err) { case codes.DeadlineExceeded, codes.Internal, codes.Unavailable, codes.DataLoss: // 异常请求错误 return false default: return true } } 如果请求正常则通过markSuccess把请求数量和请求接受数量都加一,如果请求不正常则只有请求数量会加一 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { // 判断是否触发熔断 if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 执行真正的调用 err := req() // 正常请求计数 if acceptable(err) { b.markSuccess() } else { // 异常请求计数 b.markFailure() } return err } 总结 调用端可以通过熔断机制进行自我保护,防止调用下游服务出现异常,或者耗时过长影响调用端的业务逻辑,很多功能完整的微服务框架都会内置熔断器。其实,不仅微服务调用之间需要熔断器,在调用依赖资源的时候,比如mysql、redis等也可以引入熔断器的机制。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"collection.html":{"url":"collection.html","title":"进程内缓存组件 collection.Cache","keywords":"","body":"通过 collection.Cache 进行缓存 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等,本系列文章将分别介绍go-zero框架中工具的使用及其实现原理 进程内缓存工具collection.Cache 在做服务器开发的时候,相信都会遇到使用缓存的情况,go-zero 提供的简单的缓存封装 collection.Cache,简单使用方式如下 // 初始化 cache,其中 WithLimit 可以指定最大缓存的数量 c, err := collection.NewCache(time.Minute, collection.WithLimit(10000)) if err != nil { panic(err) } // 设置缓存 c.Set(\"key\", user) // 获取缓存,ok:是否存在 v, ok := c.Get(\"key\") // 删除缓存 c.Del(\"key\") // 获取缓存,如果 key 不存在的,则会调用 func 去生成缓存 v, err := c.Take(\"key\", func() (interface{}, error) { return user, nil }) cache 实现的建的功能包括 缓存自动失效,可以指定过期时间 缓存大小限制,可以指定缓存个数 缓存增删改 缓存命中率统计 并发安全 缓存击穿 实现原理: Cache 自动失效,是采用 TimingWheel(https://github.com/zeromicro/zeromicro/blob/master/core/collection/timingwheel.go) 进行管理的 timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { key, ok := k.(string) if !ok { return } cache.Del(key) }) Cache 大小限制,是采用 LRU 淘汰策略,在新增缓存的时候会去检查是否已经超出过限制,具体代码在 keyLru 中实现 func (klru *keyLru) add(key string) { if elem, ok := klru.elements[key]; ok { klru.evicts.MoveToFront(elem) return } // Add new item elem := klru.evicts.PushFront(key) klru.elements[key] = elem // Verify size not exceeded if klru.evicts.Len() > klru.limit { klru.removeOldest() } } Cache 的命中率统计,是在代码中实现 cacheStat,在缓存命中丢失的时候自动统计,并且会定时打印使用的命中率, qps 等状态. 打印的具体效果如下 cache(proc) - qpm: 2, hit_ratio: 50.0%, elements: 0, hit: 1, miss: 1 缓存击穿包含是使用 syncx.SingleFlight(https://github.com/zeromicro/zeromicro/blob/master/core/syncx/singleflight.go) 进行实现的,就是将同时请求同一个 key 的请求, 关于 SingleFlight 后续会继续补充。 相关具体实现是在: func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { val, fresh, err := c.barrier.DoEx(key, func() (interface{}, error) { v, e := fetch() if e != nil { return nil, e } c.Set(key, v) return v, nil }) if err != nil { return nil, err } if fresh { c.stats.IncrementMiss() return val, nil } else { // got the result from previous ongoing query c.stats.IncrementHit() } return val, nil } 本文主要介绍了go-zero框架中的 Cache 工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"keywords.html":{"url":"keywords.html","title":"高效的关键词替换和敏感词过滤工具","keywords":"","body":"高效的关键词替换和敏感词过滤工具 1. 算法介绍 利用高效的Trie树建立关键词树,如下图所示,然后依次查找字符串中的相连字符是否形成树的一条路径 发现掘金上这篇文章写的比较详细,可以一读,具体原理在此不详述。 2. 关键词替换 支持关键词重叠,自动选用最短的关键词,并且只会对原始字符串只会遍历一次进行匹配替换,如果替换结果中又出现的关键词不会被二次替换(如果业务上有这种可能性,请自行对上一次的替换结果再次执行替换操作),代码示例如下: replacer := stringx.NewReplacer(map[string]string{ \"日本\": \"法国\", \"日本的首都\": \"东京\", \"东京\": \"日本的首都\", }) fmt.Println(replacer.Replace(\"日本的首都是东京\")) 可以得到: ```Plain Text 法国的首都是日本的首都 示例代码见`stringx/replace/replace.go` ## 3. 查找敏感词 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }) keywords := filter.FindKeywords(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(keywords) 可以得到: ```Plain Text [苍井空 日本AV女优 AV演员色情 AV AV演员] ## 4. 敏感词过滤 代码示例如下: ```go filter := stringx.NewTrie([]string{ \"AV演员\", \"苍井空\", \"AV\", \"日本AV女优\", \"AV演员色情\", }, stringx.WithMask('?')) // 默认替换为* safe, keywords, found := filter.Filter(\"日本AV演员兼电视、电影演员。苍井空AV女优是xx出道, 日本AV女优们最精彩的表演是AV演员色情表演\") fmt.Println(safe) fmt.Println(keywords) fmt.Println(found) 可以得到: Plain Text 日本????兼电视、电影演员。?????女优是xx出道, ??????们最精彩的表演是??????表演 [苍井空 日本AV女优 AV演员色情 AV AV演员] true 示例代码见stringx/filter/filter.go 5. Benchmark Sentences Keywords Regex go-zero 10000 10000 16min10s 27.2ms Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"loadshedding.html":{"url":"loadshedding.html","title":"服务自适应降载保护设计","keywords":"","body":"服务自适应降载保护设计 设计目的 保证系统不被过量请求拖垮 在保证系统稳定的前提下,尽可能提供更高的吞吐量 设计考虑因素 如何衡量系统负载 是否处于虚机或容器内,需要读取cgroup相关负载 用1000m表示100%CPU,推荐使用800m表示系统高负载 尽可能小的Overhead,不显著增加RT 不考虑服务本身所依赖的DB或者缓存系统问题,这类问题通过熔断机制来解决 机制设计 计算CPU负载时使用滑动平均来降低CPU负载抖动带来的不稳定,关于滑动平均见参考资料 滑动平均就是取之前连续N次值的近似平均,N取值可以通过超参beta来决定 当CPU负载大于指定值时触发降载保护机制 时间窗口机制,用滑动窗口机制来记录之前时间窗口内的QPS和RT(response time) 滑动窗口使用5秒钟50个桶的方式,每个桶保存100ms时间内的请求,循环利用,最新的覆盖最老的 计算maxQPS和minRT时需要过滤掉最新的时间没有用完的桶,防止此桶内只有极少数请求,并且RT处于低概率的极小值,所以计算maxQPS和minRT时按照上面的50个桶的参数只会算49个 满足以下所有条件则拒绝该请求 当前CPU负载超过预设阈值,或者上次拒绝时间到现在不超过1秒(冷却期)。冷却期是为了不能让负载刚下来就马上增加压力导致立马又上去的来回抖动 averageFlying > max(1, QPS*minRT/1e3) averageFlying = MovingAverage(flying) 在算MovingAverage(flying)的时候,超参beta默认取值为0.9,表示计算前十次的平均flying值 取flying值的时候,有三种做法: 请求增加后更新一次averageFlying,见图中橙色曲线 请求结束后更新一次averageFlying,见图中绿色曲线 请求增加后更新一次averageFlying,请求结束后更新一次averageFlying 我们使用的是第二种,这样可以更好的防止抖动,如图: QPS = maxPass * bucketsPerSecond maxPass表示每个有效桶里的成功的requests bucketsPerSecond表示每秒有多少个桶 1e3表示1000毫秒,minRT单位也是毫秒,QPS*minRT/1e3得到的就是平均每个时间点有多少并发请求 降载的使用 已经在rest和zrpc框架里增加了可选激活配置 CpuThreshold,如果把值设置为大于0的值,则激活该服务的自动降载机制 如果请求被drop,那么错误日志里会有dropreq关键字 参考资料 滑动平均 Sentinel自适应限流 Kratos自适应限流保护 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mapping.html":{"url":"mapping.html","title":"文本序列化和反序列化","keywords":"","body":"文本序列化和反序列化 go-zero针对文本的序列化和反序列化主要在三个地方使用 http api请求体的反序列化 http api返回体的序列化 配置文件的反序列化 本文假定读者已经定义过api文件以及修改过配置文件,如不熟悉,可参照 快速构建高并发微服务 快速构建高并发微服务 1. http api请求体的反序列化 在反序列化的过程中的针对请求数据的数据格式以及数据校验需求,go-zero实现了自己的一套反序列化机制 1.1 数据格式以订单order.api文件为例 type ( createOrderReq struct { token string `path:\"token\"` // 用户token productId string `json:\"productId\"` // 商品ID num int `json:\"num\"` // 商品数量 } createOrderRes struct { success bool `json:\"success\"` // 是否成功 } findOrderReq struct { token string `path:\"token\"` // 用户token page int `form:\"page\"` // 页数 pageSize int8 `form:\"pageSize\"` // 页大小 } findOrderRes struct { orderInfo []orderInfo `json:\"orderInfo\"` // 商品ID } orderInfo struct { productId string `json:\"productId\"` // 商品ID productName string `json:\"productName\"` // 商品名称 num int `json:\"num\"` // 商品数量 } deleteOrderReq struct { id string `path:\"id\"` } deleteOrderRes struct { success bool `json:\"success\"` // 是否成功 } ) service order { @doc( summary: 创建订单 ) @handler CreateOrderHandler post /order/add/:token(createOrderReq) returns(createOrderRes) @doc( summary: 获取订单 ) @handler FindOrderHandler get /order/find/:token(findOrderReq) returns(findOrderRes) @doc( summary: 删除订单 ) @handler: DeleteOrderHandler delete /order/:id(deleteOrderReq) returns(deleteOrderRes) } http api请求体的反序列化的tag有三种: path:http url 路径中参数反序列化 /order/add/1234567会解析出来token为1234567 form:http form表单反序列化,需要 header头添加 Content-Type: multipart/form-data /order/find/1234567?page=1&pageSize=20会解析出来token为1234567,page为1,pageSize为20 json:http request json body反序列化,需要 header头添加 Content-Type: application/json {\"productId\":\"321\",\"num\":1}会解析出来productId为321,num为1 1.2 数据校验以用户user.api文件为例 type ( createUserReq struct { age int8 `json:\"age,default=20,range=(12:100]\"` // 年龄 name string `json:\"name\"` // 名字 alias string `json:\"alias,optional\"` // 别名 sex string `json:\"sex,options=male|female\"` // 性别 avatar string `json:\"avatar,default=default.png\"` // 头像 } createUserRes struct { success bool `json:\"success\"` // 是否成功 } ) service user { @doc( summary: 创建订单 ) @handler CreateUserHandler post /user/add(createUserReq) returns(createUserRes) } 数据校验有很多种方式,包括以下但不限: age:默认不输入为20,输入则取值范围为(12:100],前开后闭 name:必填,不可为空 alias:选填,可为空 sex:必填,取值为male或female avatar:选填,默认为default.png 更多详情参见unmarshaler_test.go 2. http api返回体的序列化 使用官方默认的encoding/json包序列化,在此不再累赘 3. 配置文件的反序列化 配置文件的反序列化和http api请求体的反序列化使用同一套解析规则,可参照http api请求体的反序列化 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"mapreduce.html":{"url":"mapreduce.html","title":"并发处理工具 MapReduce","keywords":"","body":"通过MapReduce降低服务响应时间 在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了 WaitGroup 工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错 基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,该工具开箱即用,不需要做什么初始化,我们通过下图看下使用MapReduce和没使用的耗时对比: 相同的依赖,串行处理的话需要200ms,使用MapReduce后的耗时等于所有依赖中最大的耗时为100ms,可见MapReduce可以大大降低服务耗时,而且随着依赖的增加效果就会越明显,减少处理耗时的同时并不会增加服务器压力 并发处理工具MapReduce MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想 go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能 我们通过几个示例来演示MapReduce的用法 MapReduce主要有三个参数,第一个参数为generate用以生产数据,第二个参数为mapper用以对数据进行处理,第三个参数为reducer用以对mapper后的数据做聚合返回,还可以通过opts选项设置并发处理的线程数量 场景一: 某些功能的结果往往需要依赖多个服务,比如商品详情的结果往往会依赖用户服务、库存服务、订单服务等等,一般被依赖的服务都是以rpc的形式对外提供,为了降低依赖的耗时我们往往需要对依赖做并行处理 func productDetail(uid, pid int64) (*ProductDetail, error) { var pd ProductDetail err := mr.Finish(func() (err error) { pd.User, err = userRpc.User(uid) return }, func() (err error) { pd.Store, err = storeRpc.Store(pid) return }, func() (err error) { pd.Order, err = orderRpc.Order(pid) return }) if err != nil { log.Printf(\"product detail error: %v\", err) return nil, err } return &pd, nil } 该示例中返回商品详情依赖了多个服务获取数据,因此做并发的依赖处理,对接口的性能有很大的提升 场景二: 很多时候我们需要对一批数据进行处理,比如对一批用户id,效验每个用户的合法性并且效验过程中有一个出错就认为效验失败,返回的结果为效验合法的用户id func checkLegal(uids []int64) ([]int64, error) { r, err := mr.MapReduce(func(source chan 该示例中,如果check过程出现错误则通过cancel方法结束效验过程,并返回error整个效验过程结束,如果某个uid效验结果为false则最终结果不返回该uid MapReduce使用注意事项 mapper和reducer中都可以调用cancel,参数为error,调用后立即返回,返回结果为nil, error mapper中如果不调用writer.Write则item最终不会被reducer聚合 reducer中如果不调用writer.Wirte则返回结果为nil, ErrReduceNoOutput reducer为单线程,所有mapper出来的结果在这里串行聚合 实现原理分析: MapReduce中首先通过buildSource方法通过执行generate(参数为无缓冲channel)产生数据,并返回无缓冲的channel,mapper会从该channel中读取数据 func buildSource(generate GenerateFunc) chan interface{} { source := make(chan interface{}) go func() { defer close(source) generate(source) }() return source } 在MapReduceWithSource方法中定义了cancel方法,mapper和reducer中都可以调用该方法,调用后主线程收到close信号会立马返回 cancel := once(func(err error) { if err != nil { retErr.Set(err) } else { // 默认的error retErr.Set(ErrCancelWithNil) } drain(source) // 调用close(ouput)主线程收到Done信号,立马返回 finish() }) 在mapperDispatcher方法中调用了executeMappers,executeMappers消费buildSource产生的数据,每一个item都会起一个goroutine单独处理,默认最大并发数为16,可以通过WithWorkers进行设置 var wg sync.WaitGroup defer func() { wg.Wait() // 保证所有的item都处理完成 close(collector) }() pool := make(chan lang.PlaceholderType, workers) writer := newGuardedWriter(collector, done) // 将mapper处理完的数据写入collector for { select { case reducer单goroutine对数mapper写入collector的数据进行处理,如果reducer中没有手动调用writer.Write则最终会执行finish方法对output进行close避免死锁 go func() { defer func() { if r := recover(); r != nil { cancel(fmt.Errorf(\"%v\", r)) } else { finish() } }() reducer(collector, writer, cancel) }() 在该工具包中还提供了许多针对不同业务场景的方法,实现原理与MapReduce大同小异,感兴趣的同学可以查看源码学习 MapReduceVoid 功能和MapReduce类似但没有结果返回只返回error Finish 处理固定数量的依赖,返回error,有一个error立即返回 FinishVoid 和Finish方法功能类似,没有返回值 Map 只做generate和mapper处理,返回channel MapVoid 和Map功能类似,无返回 本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"metric.html":{"url":"metric.html","title":"基于prometheus的微服务指标监控","keywords":"","body":"基于prometheus的微服务指标监控 服务上线后我们往往需要对服务进行监控,以便能及早发现问题并做针对性的优化,监控又可分为多种形式,比如日志监控,调用链监控,指标监控等等。而通过指标监控能清晰的观察出服务指标的变化趋势,了解服务的运行状态,对于保证服务稳定起着非常重要的作用 prometheus是一个开源的系统监控和告警工具,支持强大的查询语言PromQL允许用户实时选择和汇聚时间序列数据,时间序列数据是服务端通过HTTP协议主动拉取获得,也可以通过中间网关来推送时间序列数据,可以通过静态配置文件或服务发现来获取监控目标 Prometheus 的架构 Prometheus 的整体架构以及生态系统组件如下图所示: Prometheus Server直接从监控目标中或者间接通过推送网关来拉取监控指标,它在本地存储所有抓取到样本数据,并对此数据执行一系列规则,以汇总和记录现有数据的新时间序列或生成告警。可以通过 Grafana 或者其他工具来实现监控数据的可视化 go-zero基于prometheus的服务指标监控 go-zero 框架中集成了基于prometheus的服务指标监控,下面我们通过go-zero官方的示例shorturl来演示是如何对服务指标进行收集监控的: 第一步需要先安装Prometheus,安装步骤请参考官方文档 go-zero默认不开启prometheus监控,开启方式很简单,只需要在shorturl-api.yaml文件中增加配置如下,其中Host为Prometheus Server地址为必填配置,Port端口不填默认9091,Path为用来拉取指标的路径默认为/metrics Prometheus: Host: 127.0.0.1 Port: 9091 Path: /metrics 编辑prometheus的配置文件prometheus.yml,添加如下配置,并创建targets.json - job_name: 'file_ds' file_sd_configs: - files: - targets.json 编辑targets.json文件,其中targets为shorturl配置的目标地址,并添加了几个默认的标签 [ { \"targets\": [\"127.0.0.1:9091\"], \"labels\": { \"job\": \"shorturl-api\", \"app\": \"shorturl-api\", \"env\": \"test\", \"instance\": \"127.0.0.1:8888\" } } ] 启动prometheus服务,默认侦听在9090端口 prometheus --config.file=prometheus.yml 在浏览器输入http://127.0.0.1:9090/,然后点击Status -> Targets即可看到状态为Up的Job,并且Lables栏可以看到我们配置的默认的标签 通过以上几个步骤我们完成了prometheus对shorturl服务的指标监控收集的配置工作,为了演示简单我们进行了手动的配置,在实际的生产环境中一般采用定时更新配置文件或者服务发现的方式来配置监控目标,篇幅有限这里不展开讲解,感兴趣的同学请自行查看相关文档 go-zero监控的指标类型 go-zero中目前在http的中间件和rpc的拦截器中添加了对请求指标的监控。 主要从请求耗时和请求错误两个维度,请求耗时采用了Histogram指标类型定义了多个Buckets方便进行分位统计,请求错误采用了Counter类型,并在http metric中添加了path标签rpc metric中添加了method标签以便进行细分监控。 接下来演示如何查看监控指标: 首先在命令行多次执行如下命令 curl -i \"http://localhost:8888/shorten?url=http://www.xiaoheiban.cn\" 打开Prometheus切换到Graph界面,在输入框中输入{path=\"/shorten\"}指令,即可查看监控指标,如下图 我们通过PromQL语法查询过滤path为/shorten的指标,结果中显示了指标名以及指标数值,其中http_server_requests_code_total指标中code值为http的状态码,200表明请求成功,http_server_requests_duration_ms_bucket中对不同bucket结果分别进行了统计,还可以看到所有的指标中都添加了我们配置的默认指标 Console界面主要展示了查询的指标结果,Graph界面为我们提供了简单的图形化的展示界面,在实际的生产环境中我们一般使用Grafana做图形化的展示 grafana可视化界面 grafana是一款可视化工具,功能强大,支持多种数据来源Prometheus、Elasticsearch、Graphite等,安装比较简单请参考官方文档,grafana默认端口3000,安装好后再浏览器输入http://localhost:3000/,默认账号和密码都为admin 下面演示如何基于以上指标进行可视化界面的绘制: 点击左侧边栏Configuration->Data Source->Add data source进行数据源添加,其中HTTP的URL为数据源的地址 点击左侧边栏添加dashboard,然后添加Variables方便针对不同的标签进行过滤筛选比如添加app变量用来过滤不同的服务 进入dashboard点击右上角Add panel添加面板,以path维度统计接口的qps 最终的效果如下所示,可以通过服务名称过滤不同的服务,面板展示了path为/shorten的qps变化趋势 总结 以上演示了go-zero中基于prometheus+grafana服务指标监控的简单流程,生产环境中可以根据实际的场景做不同维度的监控分析。现在go-zero的监控指标主要还是针对http和rpc,这对于服务的整体监控显然还是不足的,比如容器资源的监控,依赖的mysql、redis等资源的监控,以及自定义的指标监控等等,go-zero在这方面后续还会持续优化。希望这篇文章能够给您带来帮助 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"sharedcalls.html":{"url":"sharedcalls.html","title":"防止缓存击穿之进程内共享调用","keywords":"","body":"防止缓存击穿之进程内共享调用 go-zero微服务框架中提供了许多开箱即用的工具,好的工具不仅能提升服务的性能而且还能提升代码的鲁棒性避免出错,实现代码风格的统一方便他人阅读等等。 本文主要讲述进程内共享调用神器SharedCalls(v1.2.0改名为SingleFlight ) 使用场景 并发场景下,可能会有多个线程(协程)同时请求同一份资源,如果每个请求都要走一遍资源的请求过程,除了比较低效之外,还会对资源服务造成并发的压力。举一个具体例子,比如缓存失效,多个请求同时到达某服务请求某资源,该资源在缓存中已经失效,此时这些请求会继续访问DB做查询,会引起数据库压力瞬间增大。而使用SharedCalls可以使得同时多个请求只需要发起一次拿结果的调用,其他请求\"坐享其成\",这种设计有效减少了资源服务的并发压力,可以有效防止缓存击穿。 高并发场景下,当某个热点key缓存失效后,多个请求会同时从数据库加载该资源,并保存到缓存,如果不做防范,可能会导致数据库被直接打死。针对这种场景,go-zero框架中已经提供了实现,具体可参看sqlc和mongoc等实现代码。 为了简化演示代码,我们通过多个线程同时去获取一个id来模拟缓存的场景。如下: func main() { const round = 5 var wg sync.WaitGroup barrier := syncx.NewSharedCalls() wg.Add(round) for i := 0; i 运行,打印结果为: 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 837c577b1008a0db 可以看出,只要是同一个key上的同时发起的请求,都会共享同一个结果,对获取DB数据进缓存等场景特别有用,可以有效防止缓存击穿。 关键源码分析 SharedCalls interface提供了Do和DoEx两种方法的抽象 // SharedCalls接口提供了Do和DoEx两种方法 type SharedCalls interface { Do(key string, fn func() (interface{}, error)) (interface{}, error) DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error) } SharedCalls interface的具体实现sharedGroup // call代表对指定资源的一次请求 type call struct { wg sync.WaitGroup // 用于协调各个请求goroutine之间的资源共享 val interface{} // 用于保存请求的返回值 err error // 用于保存请求过程中发生的错误 } type sharedGroup struct { calls map[string]*call lock sync.Mutex } sharedGroup的Do方法 key参数:可以理解为资源的唯一标识。 fn参数:真正获取资源的方法。 处理过程分析: // 当多个请求同时使用Do方法请求资源时 func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) { // 先申请加锁 g.lock.Lock() // 根据key,获取对应的call结果,并用变量c保存 if c, ok := g.calls[key]; ok { // 拿到call以后,释放锁,此处call可能还没有实际数据,只是一个空的内存占位 g.lock.Unlock() // 调用wg.Wait,判断是否有其他goroutine正在申请资源,如果阻塞,说明有其他goroutine正在获取资源 c.wg.Wait() // 当wg.Wait不再阻塞,表示资源获取已经结束,可以直接返回结果 return c.val, c.err } // 没有拿到结果,则调用makeCall方法去获取资源,注意此处仍然是锁住的,可以保证只有一个goroutine可以调用makecall c := g.makeCall(key, fn) // 返回调用结果 return c.val, c.err } sharedGroup的DoEx方法 和Do方法类似,只是返回值中增加了布尔值表示值是调用makeCall方法直接获取的,还是取的共享成果 func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) { g.lock.Lock() if c, ok := g.calls[key]; ok { g.lock.Unlock() c.wg.Wait() return c.val, false, c.err } c := g.makeCall(key, fn) return c.val, true, c.err } sharedGroup的makeCall方法 该方法由Do和DoEx方法调用,是真正发起资源请求的方法。 // 进入makeCall的一定只有一个goroutine,因为要拿锁锁住的 func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call { // 创建call结构,用于保存本次请求的结果 c := new(call) // wg加1,用于通知其他请求资源的goroutine等待本次资源获取的结束 c.wg.Add(1) // 将用于保存结果的call放入map中,以供其他goroutine获取 g.calls[key] = c // 释放锁,这样其他请求的goroutine才能获取call的内存占位 g.lock.Unlock() defer func() { // delete key first, done later. can't reverse the order, because if reverse, // another Do call might wg.Wait() without get notified with wg.Done() g.lock.Lock() delete(g.calls, key) g.lock.Unlock() // 调用wg.Done,通知其他goroutine可以返回结果,这样本批次所有请求完成结果的共享 c.wg.Done() }() // 调用fn方法,将结果填入变量c中 c.val, c.err = fn() return c } 最后 本文主要介绍了go-zero框架中的 SharedCalls工具,对其应用场景和关键代码做了简单的梳理,希望本篇文章能给大家带来一些收获。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"sql-cache.html":{"url":"sql-cache.html","title":"DB缓存机制","keywords":"","body":"DB缓存机制 QueryRowIndex 没有查询条件到Primary映射的缓存 通过查询条件到DB去查询行记录,然后 把Primary到行记录的缓存写到redis里 把查询条件到Primary的映射保存到redis里,框架的Take方法自动做了 可能的过期顺序 查询条件到Primary的映射缓存未过期 Primary到行记录的缓存未过期 直接返回缓存行记录 Primary到行记录的缓存已过期 通过Primary到DB获取行记录,并写入缓存 此时存在的问题是,查询条件到Primary的缓存可能已经快要过期了,短时间内的查询又会触发一次数据库查询 要避免这个问题,可以让上面粗体部分第一个过期时间略长于第二个,比如5秒 查询条件到Primary的映射缓存已过期,不管Primary到行记录的缓存是否过期 查询条件到Primary的映射会被重新获取,获取过程中会自动写入新的Primary到行记录的缓存,这样两种缓存的过期时间都是刚刚设置 有查询条件到Primary映射的缓存 没有Primary到行记录的缓存 通过Primary到DB查询行记录,并写入缓存 有Primary到行记录的缓存 直接返回缓存结果 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"zrpc.html":{"url":"zrpc.html","title":"zrpc 使用介绍","keywords":"","body":"企业级RPC框架zRPC 近期比较火的开源项目go-zero是一个集成了各种工程实践的包含了Web和RPC协议的功能完善的微服务框架,今天我们就一起来分析一下其中的RPC部分zRPC。 zRPC底层依赖gRPC,内置了服务注册、负载均衡、拦截器等模块,其中还包括自适应降载,自适应熔断,限流等微服务治理方案,是一个简单易用的可直接用于生产的企业级RPC框架。 zRPC初探 zRPC支持直连和基于etcd服务发现两种方式,我们以基于etcd做服务发现为例演示zRPC的基本使用: 配置 创建hello.yaml配置文件,配置如下: Name: hello.rpc // 服务名 ListenOn: 127.0.0.1:9090 // 服务监听地址 Etcd: Hosts: - 127.0.0.1:2379 // etcd服务地址 Key: hello.rpc // 服务注册key 创建proto文件 创建hello.proto文件,并生成对应的go代码 syntax = \"proto3\"; package pb; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {} } message HelloRequest { string name = 1; } message HelloReply { string message = 1; } 生成go代码 protoc --go_out=plugins=grpc:. hello.proto Server端 package main import ( \"context\" \"flag\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/conf\" \"github.com/zeromicro/go-zero/zrpc\" \"google.golang.org/grpc\" ) type Config struct { zrpc.RpcServerConf } var cfgFile = flag.String(\"f\", \"./hello.yaml\", \"cfg file\") func main() { flag.Parse() var cfg Config conf.MustLoad(*cfgFile, &cfg) srv, err := zrpc.NewServer(cfg.RpcServerConf, func(s *grpc.Server) { pb.RegisterGreeterServer(s, &Hello{}) }) if err != nil { log.Fatal(err) } srv.Start() } type Hello struct{} func (h *Hello) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { return &pb.HelloReply{Message: \"hello \" + in.Name}, nil } Client端 package main import ( \"context\" \"log\" \"example/zrpc/pb\" \"github.com/zeromicro/go-zero/core/discov\" \"github.com/zeromicro/go-zero/zrpc\" ) func main() { client := zrpc.MustNewClient(zrpc.RpcClientConf{ Etcd: discov.EtcdConf{ Hosts: []string{\"127.0.0.1:2379\"}, Key: \"hello.rpc\", }, }) conn := client.Conn() hello := pb.NewGreeterClient(conn) reply, err := hello.SayHello(context.Background(), &pb.HelloRequest{Name: \"go-zero\"}) if err != nil { log.Fatal(err) } log.Println(reply.Message) } 启动服务,查看服务是否注册: ETCDCTL_API=3 etcdctl get hello.rpc --prefix 显示服务已经注册: hello.rpc/7587849401504590084 127.0.0.1:9090 运行客户端即可看到输出: hello go-zero 这个例子演示了zRPC的基本使用,可以看到通过zRPC构建RPC服务非常简单,只需要很少的几行代码,接下来我们继续进行探索 zRPC原理分析 下图展示zRPC的架构图和主要组成部分 zRPC主要有以下几个模块组成: discov: 服务发现模块,基于etcd实现服务发现功能 resolver: 服务注册模块,实现了gRPC的resolver.Builder接口并注册到gRPC interceptor: 拦截器,对请求和响应进行拦截处理 balancer: 负载均衡模块,实现了p2c负载均衡算法,并注册到gRPC client: zRPC客户端,负责发起请求 server: zRPC服务端,负责处理请求 这里介绍了zRPC的主要组成模块和每个模块的主要功能,其中resolver和balancer模块实现了gRPC开放的接口,实现了自定义的resolver和balancer,拦截器模块是整个zRPC的功能重点,自适应降载、自适应熔断、prometheus服务指标收集等功能都在这里实现 Interceptor模块 gRPC提供了拦截器功能,主要是对请求前后进行额外处理的拦截操作,其中拦截器包含客户端拦截器和服务端拦截器,又分为一元(Unary)拦截器和流(Stream)拦截器,这里我们主要讲解一元拦截器,流拦截器同理。 客户端拦截器定义如下: type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error 其中method为方法名,req,reply分别为请求和响应参数,cc为客户端连接对象,invoker参数是真正执行rpc方法的handler其实在拦截器中被调用执行 服务端拦截器定义如下: type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) 其中req为请求参数,info中包含了请求方法属性,handler为对server端方法的包装,也是在拦截器中被调用执行 zRPC中内置了丰富的拦截器,其中包括自适应降载、自适应熔断、权限验证、prometheus指标收集等等,由于拦截器较多,篇幅有限没法所有的拦截器给大家一一解析,这里我们主要分析两个,自适应熔断和prometheus服务监控指标收集: 内置拦截器分析 自适应熔断(breaker) 当客户端向服务端发起请求,客户端会记录服务端返回的错误,当错误达到一定的比例,客户端会自行的进行熔断处理,丢弃掉一定比例的请求以保护下游依赖,且可以自动恢复。zRPC中自适应熔断遵循《Google SRE》中过载保护策略,算法如下: requests: 总请求数量 accepts: 正常请求数量 K: 倍值 (Google SRE推荐值为2) 可以通过修改K的值来修改熔断发生的激进程度,降低K的值会使得自适应熔断算法更加激进,增加K的值则自适应熔断算法变得不再那么激进 熔断拦截器定义如下: func BreakerInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { // target + 方法名 breakerName := path.Join(cc.Target(), method) return breaker.DoWithAcceptable(breakerName, func() error { // 真正执行调用 return invoker(ctx, method, req, reply, cc, opts...) }, codes.Acceptable) } accept方法实现了Google SRE过载保护算法,判断否进行熔断 func (b *googleBreaker) accept() error { // accepts为正常请求数,total为总请求数 accepts, total := b.history() weightedAccepts := b.k * float64(accepts) // 算法实现 dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1)) if dropRatio doReq方法首先判断是否熔断,满足条件直接返回error(circuit breaker is open),不满足条件则对请求数进行累加 func (b *googleBreaker) doReq(req func() error, fallback func(err error) error, acceptable Acceptable) error { if err := b.accept(); err != nil { if fallback != nil { return fallback(err) } else { return err } } defer func() { if e := recover(); e != nil { b.markFailure() panic(e) } }() // 此处执行RPC请求 err := req() // 正常请求total和accepts都会加1 if acceptable(err) { b.markSuccess() } else { // 请求失败只有total会加1 b.markFailure() } return err } prometheus指标收集 服务监控是了解服务当前运行状态以及变化趋势的重要手段,监控依赖于服务指标的收集,通过prometheus进行监控指标的收集是业界主流方案,zRPC中也采用了prometheus来进行指标的收集 prometheus拦截器定义如下: 这个拦截器主要是对服务的监控指标进行收集,这里主要是对RPC方法的耗时和调用错误进行收集,这里主要使用了Prometheus的Histogram和Counter数据类型 func UnaryPrometheusInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) ( interface{}, error) { // 执行前记录一个时间 startTime := timex.Now() resp, err := handler(ctx, req) // 执行后通过Since算出执行该调用的耗时 metricServerReqDur.Observe(int64(timex.Since(startTime)/time.Millisecond), info.FullMethod) // 方法对应的错误码 metricServerReqCodeTotal.Inc(info.FullMethod, strconv.Itoa(int(status.Code(err)))) return resp, err } } 添加自定义拦截器 除了内置了丰富的拦截器之外,zRPC同时支持添加自定义拦截器 Client端通过AddInterceptor方法添加一元拦截器: func (rc *RpcClient) AddInterceptor(interceptor grpc.UnaryClientInterceptor) { rc.client.AddInterceptor(interceptor) } Server端通过AddUnaryInterceptors方法添加一元拦截器: func (rs *RpcServer) AddUnaryInterceptors(interceptors ...grpc.UnaryServerInterceptor) { rs.server.AddUnaryInterceptors(interceptors...) } resolver模块 zRPC服务注册架构图: zRPC中自定义了resolver模块,用来实现服务的注册功能。zRPC底层依赖gRPC,在gRPC中要想自定义resolver需要实现resolver.Builder接口: type Builder interface { Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error) Scheme() string } 其中Build方法返回Resolver,Resolver定义如下: type Resolver interface { ResolveNow(ResolveNowOptions) Close() } 在zRPC中定义了两种resolver,direct和discov,这里我们主要分析基于etcd做服务发现的discov,自定义的resolver需要通过gRPC提供了Register方法进行注册代码如下: func RegisterResolver() { resolver.Register(&dirBuilder) resolver.Register(&disBuilder) } 当我们启动我们的zRPC Server的时候,调用Start方法,会像etcd中注册对应的服务地址: func (ags keepAliveServer) Start(fn RegisterFn) error { // 注册服务地址 if err := ags.registerEtcd(); err != nil { return err } // 启动服务 return ags.Server.Start(fn) } 当我们启动zRPC客户端的时候,在gRPC内部会调用我们自定义resolver的Build方法,zRPC通过在Build方法内调用执行了resolver.ClientConn的UpdateState方法,该方法会把服务地址注册到gRPC客户端内部: func (d *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) ( resolver.Resolver, error) { hosts := strings.FieldsFunc(target.Authority, func(r rune) bool { return r == EndpointSepChar }) // 服务发现 sub, err := discov.NewSubscriber(hosts, target.Endpoint) if err != nil { return nil, err } update := func() { var addrs []resolver.Address for _, val := range subset(sub.Values(), subsetSize) { addrs = append(addrs, resolver.Address{ Addr: val, }) } // 向gRPC注册服务地址 cc.UpdateState(resolver.State{ Addresses: addrs, }) } // 监听 sub.AddListener(update) update() // 返回自定义的resolver.Resolver return &nopResolver{cc: cc}, nil } 在discov中,通过调用load方法从etcd中获取指定服务的所有地址: func (c *cluster) load(cli EtcdClient, key string) { var resp *clientv3.GetResponse for { var err error ctx, cancel := context.WithTimeout(c.context(cli), RequestTimeout) // 从etcd中获取指定服务的所有地址 resp, err = cli.Get(ctx, makeKeyPrefix(key), clientv3.WithPrefix()) cancel() if err == nil { break } logx.Error(err) time.Sleep(coolDownInterval) } var kvs []KV c.lock.Lock() for _, ev := range resp.Kvs { kvs = append(kvs, KV{ Key: string(ev.Key), Val: string(ev.Value), }) } c.lock.Unlock() c.handleChanges(key, kvs) } 并通过watch监听服务地址的变化: func (c *cluster) watch(cli EtcdClient, key string) { rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix()) for { select { case wresp, ok := 这部分主要介绍了zRPC中是如何自定义的resolver,以及基于etcd的服务发现原理,通过这部分的介绍大家可以了解到zRPC内部服务注册发现的原理,源代码比较多只是粗略的从整个流程上进行了分析,如果大家对zRPC的源码比较感兴趣可以自行进行学习 balancer模块 负载均衡原理图: 避免过载是负载均衡策略的一个重要指标,好的负载均衡算法能很好的平衡服务端资源。常用的负载均衡算法有轮训、随机、Hash、加权轮训等。但为了应对各种复杂的场景,简单的负载均衡算法往往表现的不够好,比如轮训算法当服务响应时间变长就很容易导致负载不再平衡, 因此zRPC中自定义了默认负载均衡算法P2C(Power of Two Choices),和resolver类似,要想自定义balancer也需要实现gRPC定义的balancer.Builder接口,由于和resolver类似这里不再带大家一起分析如何自定义balancer,感兴趣的朋友可以查看gRPC相关的文档来进行学习 注意,zRPC是在客户端进行负载均衡,常见的还有通过nginx中间代理的方式 zRPC框架中默认的负载均衡算法为P2C,该算法的主要思想是: 从可用节点列表中做两次随机选择操作,得到节点A、B 比较A、B两个节点,选出负载最低的节点作为被选中的节点 伪代码如下: 主要算法逻辑在Pick方法中实现: func (p *p2cPicker) Pick(ctx context.Context, info balancer.PickInfo) ( conn balancer.SubConn, done func(balancer.DoneInfo), err error) { p.lock.Lock() defer p.lock.Unlock() var chosen *subConn switch len(p.conns) { case 0: return nil, nil, balancer.ErrNoSubConnAvailable case 1: chosen = p.choose(p.conns[0], nil) case 2: chosen = p.choose(p.conns[0], p.conns[1]) default: var node1, node2 *subConn for i := 0; i = a { b++ } // 随机获取所有节点中的两个节点 node1 = p.conns[a] node2 = p.conns[b] // 效验节点是否健康 if node1.healthy() && node2.healthy() { break } } // 选择其中一个节点 chosen = p.choose(node1, node2) } atomic.AddInt64(&chosen.inflight, 1) atomic.AddInt64(&chosen.requests, 1) return chosen.conn, p.buildDoneFunc(chosen), nil } choose方法对随机选择出来的节点进行负载比较从而最终确定选择哪个节点 func (p *p2cPicker) choose(c1, c2 *subConn) *subConn { start := int64(timex.Now()) if c2 == nil { atomic.StoreInt64(&c1.pick, start) return c1 } if c1.load() > c2.load() { c1, c2 = c2, c1 } pick := atomic.LoadInt64(&c2.pick) if start-pick > forcePick && atomic.CompareAndSwapInt64(&c2.pick, pick, start) { return c2 } else { atomic.StoreInt64(&c1.pick, start) return c1 } } 上面主要介绍了zRPC默认负载均衡算法的设计思想和代码实现,那自定义的balancer是如何注册到gRPC的呢,resolver提供了Register方法来进行注册,同样balancer也提供了Register方法来进行注册: func init() { balancer.Register(newBuilder()) } func newBuilder() balancer.Builder { return base.NewBalancerBuilder(Name, new(p2cPickerBuilder)) } 注册balancer之后gRPC怎么知道使用哪个balancer呢?这里我们需要使用配置项进行配置,在NewClient的时候通过grpc.WithBalancerName方法进行配置: func NewClient(target string, opts ...ClientOption) (*client, error) { var cli client opts = append(opts, WithDialOption(grpc.WithBalancerName(p2c.Name))) if err := cli.dial(target, opts...); err != nil { return nil, err } return &cli, nil } 这部分主要介绍了zRPC中内中的负载均衡算法的实现原理以及具体的实现方式,之后介绍了zRPC是如何注册自定义的balancer以及如何选择自定义的balancer,通过这部分大家应该对负载均衡有了更进一步的认识 总结 首先,介绍了zRPC的基本使用方法,可以看到zRPC使用非常简单,只需要少数几行代码就可以构建高性能和自带服务治理能力的RPC服务,当然这里没有面面俱到的介绍zRPC的基本使用,大家可以查看相关文档进行学习 接着,介绍了zRPC的几个重要组成模块以及其实现原理,并分析了部分源码。拦截器模块是整个zRPC的重点,其中内置了丰富的功能,像熔断、监控、降载等等也是构建高可用微服务必不可少的。resolver和balancer模块自定义了gRPC的resolver和balancer,通过该部分可以了解到整个服务注册与发现的原理以及如何构建自己的服务发现系统,同时自定义负载均衡算法也变得不再神秘 最后,zRPC是一个经历过各种工程实践的RPC框架,不论是想要用于生产还是学习其中的设计模式都是一个不可多得的开源项目。希望通过这篇文章的介绍大家能够进一步了解zRPC Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"go-zero-looklook.html":{"url":"go-zero-looklook.html","title":"使用go-zero开发一个旅游系统go-zero-looklook","keywords":"","body":"使用go-zero开发一个旅游系统go-zero-looklook 因为大家都在说目前go-zero没有一个完整的项目例子,本人接触go-zero可能比较早,go-zero在大约1000start左右我就在用了,后来跟go-zero作者加了微信也熟悉了,go-zero作者非常热心以及耐心的帮我解答了很多问题,我也想积极帮助go-zero推广社区,基本是在社区群内回答大家相关的问题,因为在这个过程中发现很多人觉得go-zero没有一个完整的项目例子,作为想推动社区的一员,索性我就把内部项目删减了一些关键东西,就搞了个可用版本开源出来,主要技术栈包含如下: go-zero nginx网关 filebeat kafka go-stash elasticsearch kibana prometheus grafana jaeger go-queue asynq asynqmon dtm docker docker-compose mysql redis 项目地址 https://github.com/Mikaelemmmm/go-zero-looklook 项目文档 https://github.com/Mikaelemmmm/go-zero-looklook/tree/main/doc 项目简介 整个项目使用了go-zero开发的微服务,基本包含了go-zero以及相关go-zero作者开发的一些中间件,所用到的技术栈基本是go-zero项目组的自研组件,基本是go-zero全家桶了 另外,前端是小程序,本项目已经对接好了小程序授权登录 以及 微信支付了 ,前端看看后面是否能开源吧 项目目录结构如下: app:所有业务代码包含api、rpc以及mq(消息队列、延迟队列、定时任务) common:通用组件 error、middleware、interceptor、tool、ctxdata等 data:该项目包含该目录依赖所有中间件(mysql、es、redis、grafana等)产生的数据,此目录下的所有内容应该在git忽略文件中,不需要提交。 deploy: filebeat: docker部署filebeat配置 go-stash:go-stash配置 nginx: nginx网关配置 prometheus : prometheus配置 script: gencode:生成api、rpc,以及创建kafka语句,复制粘贴使用 mysql:生成model的sh工具 goctl: 该项目goctl的template,goctl生成自定义代码模版,tempalte用法可参考go-zero文档,复制到家目录下.goctl即可, 该项目用到goctl版本是v1.2.3 doc : 该项目系列文档 系统架构图 业务架构图 网关 nginx做网关,使用nginx的auth模块,调用后端的identity服务统一鉴权,业务内部不鉴权,如果涉及到业务资金比较多也可以在业务中进行二次鉴权,为了安全嘛。 另外,很多同学觉得nginx做网关不太好,这块原理基本一样,可以自行替换成apisix、kong等 开发模式 本项目使用的是微服务开发,api (http) + rpc(grpc) , api充当聚合服务,复杂、涉及到其他业务调用的统一写在rpc中,如果一些不会被其他服务依赖使用的简单业务,可以直接写在api的logic中 日志 关于日志,统一使用filebeat收集,上报到kafka中,由于logstash懂得都懂,资源占用太夸张了,这里使用了go-stash替换了logstash 链接:https://github.com/kevwan/go-stash , go-stash是由go-zero开发团队开发的,性能很高不占资源,主要代码量没多少,只需要配置就可以使用,很简单。它是吧kafka数据源同步到elasticsearch中,默认不支持elasticsearch账号密码,我fork了一份修改了一下,很简单支持了账号、密码 监控 监控采用prometheus,这个go-zero原生支持,只需要配置就可以了,这里可以看项目中的配置 链路追踪 go-zero默认jaeger、zipkin支持,只需要配置就可以了,可以看配置 消息队列 消息队列使用的是go-zero开发团队开发的go-queue,链接:https://github.com/zeromicro/go-queue 这里使用可kq,kq是基于kafka做的高性能消息队列 通用go-queue中也有dq,是延迟队列,不过当前项目没有使用dq 延迟队列、定时任务 延迟队列、定时任务本项目使用的是asynq , google团队给予redis开发的简单中间件, 当然了asynq也支持消息队列,你也可也把kq消息队列替换成这个,毕竟只需要redis不需要在去维护一个kafka也是不错的 链接:https://github.com/hibiken/asynq 分布式事务 分布式事务准备使用的是dtm, 嗯 ,很舒服,之前我写过一篇 \"go-zero对接分布式事务dtm保姆式教程\" 链接地址:https://github.com/Mikaelemmmm/gozerodtm , 本项目目前还未使用到,后续准备直接集成就好了,如果读者使用直接去看那个源码就行了 部署 部署的话,目前这个直接使用docker可以部署整套技术栈,如果上k8s的话 ,最简单直接用阿里云的吧 我说下思路,这个后续会出一个基于阿里云效的部署到k8s文档教程,自己搭建一个gitlab、jenkins、harbor去做的话太费时间了 1、将代码放在阿里云效(当然你整到gitlab也行) 2、在阿里云效创建流水线,基本是一个服务一个流水线了 3、流水线步骤 : ​ 拉取代码--->ci检测(这里可以省略哈,自己看着办)--->构建镜像(go-zero官方有Dockerfile还有教程,别告诉我不会)-->推送到阿里云镜像服务--->使用kubectl去阿里云k8s拉取镜像(ack、ask都行,ask无法使用daemonset 不能用filebeat)---->ok了 另外, 如果你想自己基于gitlab、jenkins、harbor去做的话,嗯 自己去找运维弄吧,我之前也写过一个教程,有空在整吧老哥们!! Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "},"faq.html":{"url":"faq.html","title":"FAQ","keywords":"","body":"常见问题集合 goctl安装了执行命令却提示 command not found: goctl 字样。 如果你通过 go get 方式安装,那么 goctl 应该位于 $GOPATH 中, 你可以通过 go env GOPATH 查看完整路径,不管你的 goctl 是在 $GOPATH中, 还是在其他目录,出现上述问题的原因就是 goctl 所在目录不在 PATH (环境变量)中所致。 rpc怎么调用 该问题可以参考快速开始中的rpc编写与调用介绍,其中有rpc调用的使用逻辑。 proto使用了import,goctl命令需要怎么写。 goctl 对于import的proto指定 BasePath 提供了 protoc 的flag映射,即 --proto_path, -I, goctl 会将此flag值传递给 protoc. 假设 base.proto 的被main proto 引入了,为什么不生能生成base.pb.go。 对于 base.proto 这种类型的文件,一般都是开发者有message复用的需求,他的来源不止有开发者自己编写的proto文件, 还有可能来源于 google.golang.org/grpc 中提供的一些基本的proto,比如 google/protobuf/any.proto, 如果由 goctl 来生成,那么就失去了集中管理这些proto的意义。 model怎么控制缓存时间 在 sqlc.NewNodeConn 的时候可以通过可选参数 cache.WithExpiry 传递,如缓存时间控制为1天,代码如下: sqlc.NewNodeConn(conn,redis,cache.WithExpiry(24*time.Hour)) jwt鉴权怎么实现 请参考jwt鉴权 api中间件怎么使用 请参考中间件 怎么关闭输出的统计日志(stat)? logx.DisableStat() rpc直连与服务发现连接模式写法 // mode1: 集群直连 // conf:=zrpc.NewDirectClientConf([]string{\"ip:port\"},\"app\",\"token\") // mode2: etcd 服务发现 // conf:=zrpc.NewEtcdClientConf([]string{\"ip:port\"},\"key\",\"app\",\"token\") // client, _ := zrpc.NewClient(conf) // mode3: ip直连mode // client, _ := zrpc.NewClientWithTarget(\"127.0.0.1:8888\") grpc 客户端设置消息大小限制 修改grpc消息大小限制,需要 服务端 和 客户端 都设置 // 需要 grpc 方法里面添加配置项:grpc.MaxCallRecvMsgSize(bytes int)、grpc.MaxCallSendMsgSize(bytes int) // 示例如下:设置 UserRpc.List 列表消息大小限制为 8MB // l.svcCtx.UserRpc.List(l.ctx, &listReq, grpc.MaxCallRecvMsgSize(1024*1024*8), grpc.MaxCallSendMsgSize(1024*1024*8)) faq会不定期更新大家遇到的问题,也欢迎大家把常见问题通过pr写在这里。 Copyright © 2019-2021 go-zero all right reserved,powered by GitbookLast UpdateTime: 2024-03-02 02:15:50 "}} \ No newline at end of file