├── cmd ├── README.md ├── srv │ ├── user.go │ ├── socket.go │ └── account.go ├── api │ ├── backend.go │ └── frontend.go └── cmd │ ├── version.go │ └── root.go ├── cinit ├── README.md ├── mongo.go ├── const.go ├── kafka.go ├── redis.go ├── log.go ├── mertrics.go ├── postgres.go ├── mysql.go └── tracer.go ├── deployments ├── config │ ├── mysql │ │ ├── init.sql │ │ ├── my.cnf │ │ └── user.sql │ ├── postgres │ │ ├── createdb.sql │ │ ├── pg_hba.conf │ │ └── init.sql.bak │ └── swagger │ │ └── srv │ │ └── account │ │ └── proto │ │ └── account.swagger.json ├── k8s │ ├── srv_user │ │ ├── network │ │ │ ├── virtual-service.yaml │ │ │ └── destination-rule.yaml │ │ └── dev.yaml │ ├── srv_socket │ │ ├── network │ │ │ ├── virtual-service.yaml │ │ │ └── destination-rule.yaml │ │ └── dev.yaml │ ├── gateway.yaml │ ├── jaeger-svc.yaml │ ├── api_backend │ │ ├── network │ │ │ ├── destination-rule.yaml │ │ │ └── virtual-service.yaml │ │ └── dev.yaml │ └── api_frontend │ │ ├── network │ │ ├── destination-rule.yaml │ │ └── virtual-service.yaml │ │ └── dev.yaml └── docker-compose-bak.yml ├── internal ├── utils │ ├── time_test.go │ ├── fn.go │ ├── convert_test.go │ ├── mysql.go │ ├── log_test.go │ ├── page.go │ ├── time.go │ └── convert.go ├── kafka │ ├── kafka_example_test.go │ ├── producer.go │ ├── init.go │ └── consumer.go ├── sqlupdate │ ├── testdata │ │ ├── 20190828_002.sql │ │ ├── 20190828_003.sql │ │ ├── 20190828_004.sql │ │ ├── record.json │ │ └── 20190828_001.sql │ ├── sqlupdate_test.go │ └── sqlupdate.go ├── jwt │ ├── jwt_test.go │ └── jwt.go ├── wrapper │ ├── recovery.go │ └── logging.go ├── metrics │ ├── options.go │ ├── prometheus │ │ ├── options.go │ │ └── metrics.go │ ├── README.md │ └── metrics.go ├── trace │ ├── options.go │ ├── trace.go.bak │ └── config.go ├── gateway │ ├── gateway.go │ ├── handlers.go │ └── server.go ├── errors │ └── errors.go └── api │ ├── middle.go │ └── errors.go ├── srv ├── README.md ├── account │ ├── sqlupdate │ │ ├── 20190828_002.sql │ │ ├── 20190828_004.sql │ │ ├── 20190828_003.sql │ │ ├── record.json │ │ └── 20190828_001.sql │ ├── README.md │ ├── proto │ │ ├── protoc-gen-swagger │ │ │ └── options │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── annotations.proto │ │ │ │ └── annotations.pb.go │ │ ├── google │ │ │ ├── api │ │ │ │ ├── annotations.proto │ │ │ │ └── httpbody.proto │ │ │ └── rpc │ │ │ │ └── status.proto │ │ ├── account.proto │ │ └── account.swagger.json │ ├── account.go │ ├── versionupdate.go │ ├── run.go │ └── model.go ├── user │ ├── msg_queue.go │ ├── common_cache.go │ ├── proto │ │ ├── protoc-gen-swagger │ │ │ └── options │ │ │ │ ├── BUILD.bazel │ │ │ │ ├── annotations.proto │ │ │ │ └── annotations.pb.go │ │ ├── google │ │ │ ├── api │ │ │ │ ├── annotations.proto │ │ │ │ └── httpbody.proto │ │ │ └── rpc │ │ │ │ └── status.proto │ │ └── user.proto │ ├── cache.go │ ├── run.go │ └── user.go └── socket │ ├── run.go │ └── asset │ └── index.html ├── api ├── README.md ├── frontend │ ├── user.go │ └── run.go └── backend │ ├── run.go │ └── user.go ├── scripts ├── README.md ├── k8schange.sh ├── run.sh ├── check.sh ├── builddata.sh ├── version.sh ├── VARIABLES..md ├── pprof.sh ├── docker-compose.sh ├── INSTALL.md ├── .variables.sh ├── proto.sh ├── dockerfile.sh ├── build.sh └── install.sh ├── .travis.yml ├── version └── version.go ├── .gitignore ├── .goreleaser.yml ├── LICENSE ├── pkg └── pprof │ └── pprof.go ├── .golangci.yml ├── go.mod ├── docs └── godoc2md │ └── account.md └── Makefile /cmd/README.md: -------------------------------------------------------------------------------- 1 | ## 入口 -------------------------------------------------------------------------------- /cinit/README.md: -------------------------------------------------------------------------------- 1 | ## 公共配置和初始化 -------------------------------------------------------------------------------- /cinit/mongo.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | -------------------------------------------------------------------------------- /deployments/config/mysql/init.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /internal/utils/time_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | -------------------------------------------------------------------------------- /internal/kafka/kafka_example_test.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | -------------------------------------------------------------------------------- /srv/README.md: -------------------------------------------------------------------------------- 1 | ## 服务 2 | 3 | ### user 用户服务 4 | 5 | ### socket 通知服务 6 | -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | ## 接口 2 | 3 | ### backend 后台接口 4 | 5 | ### frontend 前端接口 6 | 7 | 8 | -------------------------------------------------------------------------------- /cinit/const.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | const ( 4 | TopicSrvKeyChange = "user_msg_change" 5 | ) 6 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | ## scripts 2 | 3 | ### 目的 4 | 5 | 存放构建,安装,部署的脚本,使得根目录的Makefile文件尽量小,简单 6 | 7 | -------------------------------------------------------------------------------- /srv/account/sqlupdate/20190828_002.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (5,1,23.0,5); -------------------------------------------------------------------------------- /srv/account/sqlupdate/20190828_004.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (8,1,23.0,5); 3 | -------------------------------------------------------------------------------- /internal/sqlupdate/testdata/20190828_002.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (5,1,23.0,5); -------------------------------------------------------------------------------- /internal/sqlupdate/testdata/20190828_003.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (6,1,23.0,5); -------------------------------------------------------------------------------- /internal/sqlupdate/testdata/20190828_004.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (7,1,23.0,5); -------------------------------------------------------------------------------- /scripts/k8schange.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | source scripts/.variables.sh 5 | 6 | sed -i "/- name: RAND_NUM/{ n;s/\(value: \).*/\1num$RANDOM/ }" $1.yaml 7 | -------------------------------------------------------------------------------- /srv/account/sqlupdate/20190828_003.sql: -------------------------------------------------------------------------------- 1 | -- 插入一条语句 2 | insert into account(user_id,account_level,balance,account_status) values (6,1,23.0,5); 3 | -- 开启拓展 4 | create extension pg_trgm; -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "start" 6 | 7 | echo "$*" 8 | args= 9 | for i in $@; do 10 | args=$i" " 11 | done 12 | echo $args 13 | $1 $args 14 | -------------------------------------------------------------------------------- /deployments/config/mysql/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user=mysql 3 | default-storage-engine=INNODB 4 | character-set-server=utf8 5 | [client] 6 | default-character-set=utf8 7 | [mysql] 8 | default-character-set=utf8 -------------------------------------------------------------------------------- /cmd/srv/user.go: -------------------------------------------------------------------------------- 1 | //+build srv_user 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/xiaomeng79/istio-micro/cmd/cmd" 7 | "github.com/xiaomeng79/istio-micro/srv/user" 8 | _ "github.com/xiaomeng79/istio-micro/version" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | user.Run() 14 | } 15 | -------------------------------------------------------------------------------- /deployments/k8s/srv_user/network/virtual-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: srv-user 5 | spec: 6 | hosts: 7 | - srv-user 8 | http: 9 | - route: 10 | - destination: 11 | host: srv-user 12 | subset: v1 13 | 14 | -------------------------------------------------------------------------------- /cmd/srv/socket.go: -------------------------------------------------------------------------------- 1 | //+build srv_socket 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/xiaomeng79/istio-micro/cmd/cmd" 7 | "github.com/xiaomeng79/istio-micro/srv/socket" 8 | _ "github.com/xiaomeng79/istio-micro/version" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | socket.Run() 14 | } 15 | -------------------------------------------------------------------------------- /cmd/api/backend.go: -------------------------------------------------------------------------------- 1 | //+build api_backend 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/xiaomeng79/istio-micro/api/backend" 7 | "github.com/xiaomeng79/istio-micro/cmd/cmd" 8 | _ "github.com/xiaomeng79/istio-micro/version" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | backend.Run() 14 | } 15 | -------------------------------------------------------------------------------- /cmd/api/frontend.go: -------------------------------------------------------------------------------- 1 | //+build api_frontend 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/xiaomeng79/istio-micro/api/frontend" 7 | "github.com/xiaomeng79/istio-micro/cmd/cmd" 8 | _ "github.com/xiaomeng79/istio-micro/version" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | frontend.Run() 14 | } 15 | -------------------------------------------------------------------------------- /cmd/srv/account.go: -------------------------------------------------------------------------------- 1 | //+build srv_account 2 | 3 | package main 4 | 5 | import ( 6 | "github.com/xiaomeng79/istio-micro/cmd/cmd" 7 | "github.com/xiaomeng79/istio-micro/srv/account" 8 | _ "github.com/xiaomeng79/istio-micro/version" 9 | ) 10 | 11 | func main() { 12 | cmd.Execute() 13 | account.Run() 14 | } 15 | -------------------------------------------------------------------------------- /deployments/k8s/srv_socket/network/virtual-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: srv-socket 5 | spec: 6 | hosts: 7 | - srv-socket 8 | http: 9 | - route: 10 | - destination: 11 | host: srv-socket 12 | subset: v1 13 | 14 | -------------------------------------------------------------------------------- /deployments/k8s/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: user-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use istio default controller 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" 15 | 16 | -------------------------------------------------------------------------------- /deployments/k8s/jaeger-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: jaeger-query-wai 5 | namespace: istio-system 6 | labels: 7 | app: jaeger 8 | spec: 9 | ports: 10 | - port: 16686 11 | protocol: TCP 12 | targetPort: 16686 13 | selector: 14 | app: jaeger 15 | # type: LoadBalancer 16 | type: NodePort -------------------------------------------------------------------------------- /cinit/kafka.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/xiaomeng79/istio-micro/internal/kafka" 7 | ) 8 | 9 | var Kf *kafka.Kafka 10 | 11 | // 初始化连接 12 | func KafkaInit() { 13 | addrs := strings.Split(Config.Kafka.Addrs, ",") 14 | Kf = kafka.NewKafka(addrs) 15 | } 16 | 17 | // 关闭 18 | func KafkaClose() { 19 | Kf.Close() 20 | } 21 | -------------------------------------------------------------------------------- /cmd/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/xiaomeng79/istio-micro/version" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var versionCmd = &cobra.Command{ 10 | Use: "version", 11 | Short: "this is a version", 12 | Long: `this is a version explain`, 13 | Run: func(cmd *cobra.Command, args []string) { 14 | version.Ver() 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /deployments/k8s/srv_user/network/destination-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: srv-user 5 | spec: 6 | host: srv-user 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | # - name: v2 12 | # labels: 13 | # version: v2 14 | # - name: v3 15 | # labels: 16 | # version: v3 17 | -------------------------------------------------------------------------------- /deployments/k8s/api_backend/network/destination-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: api-backend 5 | spec: 6 | host: api-backend 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | # - name: v2 12 | # labels: 13 | # version: v2 14 | # - name: v3 15 | # labels: 16 | # version: v3 17 | -------------------------------------------------------------------------------- /deployments/k8s/srv_socket/network/destination-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: srv-socket 5 | spec: 6 | host: srv-socket 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | # - name: v2 12 | # labels: 13 | # version: v2 14 | # - name: v3 15 | # labels: 16 | # version: v3 17 | -------------------------------------------------------------------------------- /deployments/k8s/api_frontend/network/destination-rule.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: api-frontend 5 | spec: 6 | host: api-frontend 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | # - name: v2 12 | # labels: 13 | # version: v2 14 | # - name: v3 15 | # labels: 16 | # version: v3 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | language: go 3 | 4 | go: 5 | - "1.11.x" 6 | 7 | env: 8 | - GO111MODULE=on 9 | 10 | before_install: 11 | #- go get github.com/stretchr/testify 12 | #- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi 13 | 14 | script: 15 | make onlinetest; 16 | 17 | after_success: 18 | - bash <(curl -s https://codecov.io/bash) -t 33bf0fdd-49b7-4a69-bfd9-3671d56af86f -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | var ( 9 | Version string 10 | GoVersion string 11 | GitCommit string 12 | BuiltTime string 13 | ) 14 | 15 | func Ver() { 16 | fmt.Printf("Version: %s\n", Version) 17 | fmt.Printf("Go Version: %s\n", GoVersion) 18 | fmt.Printf("Git Commit: %s\n", GitCommit) 19 | fmt.Printf("Built Time: %s\n", BuiltTime) 20 | os.Exit(0) 21 | } 22 | -------------------------------------------------------------------------------- /scripts/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source scripts/.variables.sh 4 | 5 | cloc_test(){ 6 | cd `pwd` && \ 7 | echo "圈复杂度检查前20:" && \ 8 | ls -d */ | grep -v vendor | xargs gocyclo -top 20 && \ 9 | ls -d */ | grep -v vendor | xargs gocyclo | awk '{sum+=$$1}END{printf("总圈复杂度: %s\n", sum)}' 10 | # echo "统计代码行数:\n" && \ 11 | # ls -d */ | grep -v vendor | xargs cloc --by-file 12 | } 13 | cloc_test -------------------------------------------------------------------------------- /scripts/builddata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | source scripts/.variables.sh 6 | 7 | #go-bindata -o data/bindata.go -pkg data data/*.json 8 | 9 | statik -src=`pwd`/srv/socket/asset -dest=`pwd`/srv/socket 10 | 11 | #更新的sql 12 | #statik -src=`pwd`/srv/account/sqlupdate -dest=`pwd`/srv/account 13 | 14 | #更新sql数据拷贝 15 | accountsql=`pwd`/deployments/bin/srv_account 16 | mkdir -p ${accountsql} && cp -r `pwd`/srv/account/sqlupdate ${accountsql} -------------------------------------------------------------------------------- /internal/utils/fn.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | type vFn func() error 10 | 11 | // 验证 12 | func V(fns ...vFn) error { 13 | for _, fn := range fns { 14 | err := fn() 15 | if err != nil { 16 | return err 17 | } 18 | } 19 | return nil 20 | } 21 | 22 | func EightBitRand() string { 23 | _rand := rand.New(rand.NewSource(time.Now().UnixNano())) 24 | return fmt.Sprintf("%08d", _rand.Int31n(100000000)) 25 | } 26 | -------------------------------------------------------------------------------- /internal/sqlupdate/testdata/record.json: -------------------------------------------------------------------------------- 1 | { 2 | "project":"test", 3 | "update":[ 4 | {"version":"0.0.1","author":"xiaomeng79","file":"./testdata/20190828_001.sql","date":"2019-08-27"}, 5 | {"version":"1.0.0","author":"xiaomeng79","file":"./testdata/20190828_002.sql","date":"2019-08-28"}, 6 | {"version":"0.1.2","author":"xiaomeng79","file":"./testdata/20190828_003.sql","date":"2019-08-28"}, 7 | {"version":"1.0.2","author":"xiaomeng79","file":"./testdata/20190828_004.sql","date":"2019-08-28"} 8 | ] 9 | } -------------------------------------------------------------------------------- /srv/account/README.md: -------------------------------------------------------------------------------- 1 | ## account服务 2 | 3 | #### 介绍 4 | 这个服务是用户的账户服务,主要用来演示使用postgres,根据版本号升级对应的数据库变更记录 5 | 6 | #### sql增量更新 7 | 1. 系统版本查看 `make next-version `下一个版本号 8 | 2. 在当前项目`sqlupdate`目录下`record.json`文件中增加下一个版本号的sql变更记录,变更sql写到文件如:`./sqlupdate/20190828_002.sql` 9 | ```bash 10 | {"version":"0.0.2","author":"xiaomeng79","file":"./sqlupdate/20190828_002.sql","date":"2019-08-28"} 11 | ``` 12 | 3. git 打标记 `git tag 0.0.2` 13 | 4. 编译程序 14 | 15 | **详细实现在run.go下的execUpdateSQL方法** 16 | 17 | #### TODO 18 | - 逻辑订阅 -------------------------------------------------------------------------------- /srv/account/sqlupdate/record.json: -------------------------------------------------------------------------------- 1 | { 2 | "project":"srv-account", 3 | "update":[ 4 | {"version":"0.0.1","author":"xiaomeng79","file":"./sqlupdate/20190828_001.sql","date":"2019-08-27"}, 5 | {"version":"0.0.2","author":"xiaomeng79","file":"./sqlupdate/20190828_002.sql","date":"2019-08-28"}, 6 | {"version":"0.1.3","author":"xiaomeng79","file":"./sqlupdate/20190828_003.sql","date":"2019-08-28"}, 7 | {"version":"0.1.4","author":"xiaomeng79","file":"./sqlupdate/20190828_004.sql","date":"2019-08-28"} 8 | ] 9 | } -------------------------------------------------------------------------------- /internal/jwt/jwt_test.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func ExampleEncode() { 9 | s, err := Encode(Msg{1, "meng"}) 10 | if err != nil { 11 | fmt.Printf("%v\n", err) 12 | } 13 | ss := strings.Split(s, ".") 14 | fmt.Println(ss[0]) 15 | r2 := Msg{} 16 | r2, err = Decode(s) 17 | if err != nil { 18 | fmt.Printf("%v\n", err) 19 | } 20 | fmt.Println(r2.UserID) 21 | fmt.Println(r2.UserName) 22 | // OutPut: 23 | // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 24 | // 1 25 | // meng 26 | } 27 | -------------------------------------------------------------------------------- /cmd/cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var rootCmd = &cobra.Command{ 11 | Use: "main", 12 | Short: "this is a demo istio micro", 13 | Long: `this is a demo istio micro ,has many`, 14 | Run: func(cmd *cobra.Command, args []string) { 15 | // Do Stuff Here 16 | }, 17 | } 18 | 19 | func cmdInit() { 20 | rootCmd.AddCommand(versionCmd) 21 | } 22 | 23 | func Execute() { 24 | cmdInit() 25 | if err := rootCmd.Execute(); err != nil { 26 | fmt.Println(err) 27 | os.Exit(1) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cinit/redis.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "github.com/go-redis/redis" 5 | "github.com/xiaomeng79/go-log" 6 | ) 7 | 8 | var RedisCli *redis.Client 9 | 10 | func redisInit() { 11 | RedisCli = redis.NewClient(&redis.Options{ 12 | Addr: Config.Redis.Addr, 13 | Password: Config.Redis.Password, 14 | DB: Config.Redis.Db, 15 | }) 16 | 17 | _, err := RedisCli.Ping().Result() 18 | if err != nil { 19 | log.Fatal(err.Error()) 20 | } 21 | } 22 | 23 | func redisClose() { 24 | err := RedisCli.Close() 25 | if err != nil { 26 | log.Error(err.Error()) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /internal/utils/convert_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | var ( 10 | oddsData = []struct { 11 | o1 float64 12 | o2 float64 13 | b bool 14 | }{ 15 | {o1: 3.65441100, o2: 3.2312541, b: false}, 16 | {o1: 3.65441100, o2: 3.65331100, b: true}, 17 | {o1: 3.65441100, o2: 2.65331100, b: false}, 18 | {o1: 3.610000, o2: 3.610000, b: true}, 19 | } 20 | ) 21 | 22 | func TestOddsCompute(t *testing.T) { 23 | for _, v := range oddsData { 24 | assert.Equal(t, v.b, OddsCompute(v.o1, v.o2)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deployments/config/postgres/createdb.sql: -------------------------------------------------------------------------------- 1 | -- 创建数据库 2 | create database test with template template0 lc_collate "zh_CN.UTF8" lc_ctype "zh_CN.UTF8" encoding 'UTF8'; 3 | \c test 4 | 5 | -- 增加系统表 6 | CREATE TABLE public.sys_info 7 | ( 8 | id serial PRIMARY KEY NOT NULL, 9 | name varchar(50) DEFAULT '' NOT NULL, 10 | version varchar(20) DEFAULT '0.0.0' NOT NULL 11 | ); 12 | COMMENT ON COLUMN public.sys_info.name IS '网站名称'; 13 | COMMENT ON COLUMN public.sys_info.version IS '版本号'; 14 | COMMENT ON TABLE public.sys_info IS '系统信息表'; 15 | 16 | -- 添加版本号 17 | INSERT INTO public.sys_info(name,version) values('账户接口','0.0.0') -------------------------------------------------------------------------------- /internal/utils/mysql.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "database/sql" 5 | "errors" 6 | ) 7 | 8 | // 影响结果判断 9 | func R(result sql.Result, err error) error { 10 | if err != nil { 11 | return err 12 | } 13 | res, err := result.RowsAffected() 14 | if err != nil { 15 | return err 16 | } 17 | if res <= 0 { 18 | return errors.New("更新失败") 19 | } 20 | return nil 21 | } 22 | 23 | // ID 24 | func ID(result sql.Result, err error) (int64, error) { 25 | if err != nil { 26 | return 0, err 27 | } 28 | id, err := result.LastInsertId() 29 | if err != nil { 30 | return 0, err 31 | } 32 | return id, nil 33 | } 34 | -------------------------------------------------------------------------------- /cinit/log.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "github.com/xiaomeng79/go-log" 5 | "github.com/xiaomeng79/go-log/conf" 6 | "github.com/xiaomeng79/go-log/plugins/zaplog" 7 | ) 8 | 9 | // 初始化日志,可以再这里初始化不同日志引擎的日志 、、 zap logrous// 初始化zap// 设置日志引擎为刚初始化的 10 | func logInit() { 11 | log.SetLogger(zaplog.New( 12 | conf.WithProjectName(Config.Service.Name), 13 | conf.WithLogPath(Config.Log.Path), 14 | conf.WithLogName(Config.Service.Name), 15 | conf.WithMaxAge(Config.Log.MaxAge), 16 | conf.WithMaxSize(Config.Log.MaxSize), 17 | conf.WithIsStdOut(Config.Log.IsStdOut), 18 | conf.WithLogLevel(Config.Log.LogLevel), 19 | )) 20 | } 21 | -------------------------------------------------------------------------------- /srv/user/msg_queue.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/xiaomeng79/istio-micro/cinit" 7 | 8 | "github.com/Shopify/sarama" 9 | "github.com/xiaomeng79/go-log" 10 | ) 11 | 12 | func msgNotify(ctx context.Context, msgStr string) { 13 | topic := cinit.TopicSrvKeyChange 14 | msg := &sarama.ProducerMessage{ 15 | Topic: topic, 16 | Value: sarama.ByteEncoder(msgStr), 17 | // Partition:0, 18 | } 19 | part, offset, err := cinit.Kf.SyncProducer(msg) 20 | if err != nil { 21 | log.Error("msg notify error:"+err.Error(), ctx) 22 | } 23 | log.Infof("topic:%s,part:%d,offset:%d", topic, part, offset, ctx) 24 | } 25 | -------------------------------------------------------------------------------- /internal/wrapper/recovery.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "context" 5 | "runtime/debug" 6 | 7 | "github.com/xiaomeng79/go-log" 8 | "google.golang.org/grpc" 9 | "google.golang.org/grpc/codes" 10 | "google.golang.org/grpc/status" 11 | ) 12 | 13 | func RecoveryUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 14 | defer func() { 15 | if e := recover(); e != nil { 16 | debug.PrintStack() 17 | err = status.Errorf(codes.Internal, "Panic err: %v", e) 18 | log.Errorf("panic err:%v", e, ctx) 19 | } 20 | }() 21 | return handler(ctx, req) 22 | } 23 | -------------------------------------------------------------------------------- /scripts/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | VERSION="$(git describe --abbrev=0 --tags)" 6 | VERSION=${VERSION:-'0.0.0'} 7 | 8 | MAJOR="${VERSION%%.*}"; VERSION="${VERSION#*.}" 9 | MINOR="${VERSION%%.*}"; VERSION="${VERSION#*.}" 10 | PATCH="${VERSION%%.*}"; VERSION="${VERSION#*.}" 11 | 12 | echo "当前版本为:"$(git describe --abbrev=0 --tags) 13 | 14 | PATCH="$((PATCH+1))" 15 | 16 | #TAG="${1}" 17 | 18 | #if [ "${TAG}" = "" ]; then 19 | # TAG="${MAJOR}.${MINOR}.${PATCH}" 20 | #fi 21 | TAG="${MAJOR}.${MINOR}.${PATCH}" 22 | 23 | echo "下一个版本为:" ${TAG} 24 | 25 | export Version=$(git describe --abbrev=0 --tags) 26 | # 27 | #git tag -a -m "Relase ${TAG}" "${TAG}" 28 | -------------------------------------------------------------------------------- /internal/sqlupdate/testdata/20190828_001.sql: -------------------------------------------------------------------------------- 1 | 2 | -- 创建账户表 3 | SET client_encoding = 'UTF8'; 4 | 5 | CREATE TABLE public.account ( 6 | id serial PRIMARY KEY NOT NULL, 7 | user_id integer DEFAULT 0 NOT NULL, 8 | account_level smallint DEFAULT 0 NOT NULL, 9 | balance numeric(18,2) DEFAULT 0.00 NOT NULL, 10 | account_status smallint DEFAULT 0 NOT NULL 11 | ); 12 | 13 | COMMENT ON TABLE public.account IS '账户表'; 14 | 15 | COMMENT ON COLUMN public.account.user_id IS '用户id'; 16 | 17 | COMMENT ON COLUMN public.account.account_level IS '账户级别'; 18 | 19 | COMMENT ON COLUMN public.account.balance IS '账户余额'; 20 | 21 | COMMENT ON COLUMN public.account.account_status IS '账户状态'; 22 | -------------------------------------------------------------------------------- /internal/metrics/options.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/rcrowley/go-metrics" 5 | ) 6 | 7 | type Option func(c *Options) 8 | 9 | type Options struct { 10 | Registry metrics.Registry 11 | Prefix string 12 | } 13 | 14 | func Registry(r metrics.Registry) Option { 15 | return func(o *Options) { 16 | o.Registry = r 17 | } 18 | } 19 | 20 | func Prefix(p string) Option { 21 | return func(o *Options) { 22 | o.Prefix = p 23 | } 24 | } 25 | 26 | func applyOptions(options ...Option) Options { 27 | opts := Options{ 28 | Registry: metrics.DefaultRegistry, 29 | } 30 | 31 | for _, option := range options { 32 | option(&opts) 33 | } 34 | 35 | return opts 36 | } 37 | -------------------------------------------------------------------------------- /srv/account/sqlupdate/20190828_001.sql: -------------------------------------------------------------------------------- 1 | 2 | -- 创建账户表 3 | SET client_encoding = 'UTF8'; 4 | 5 | CREATE TABLE IF NOT EXISTS public.account ( 6 | id serial PRIMARY KEY NOT NULL, 7 | user_id integer DEFAULT 0 NOT NULL, 8 | account_level smallint DEFAULT 0 NOT NULL, 9 | balance numeric(18,2) DEFAULT 0.00 NOT NULL, 10 | account_status smallint DEFAULT 0 NOT NULL 11 | ); 12 | 13 | COMMENT ON TABLE public.account IS '账户表'; 14 | 15 | COMMENT ON COLUMN public.account.user_id IS '用户id'; 16 | 17 | COMMENT ON COLUMN public.account.account_level IS '账户级别'; 18 | 19 | COMMENT ON COLUMN public.account.balance IS '账户余额'; 20 | 21 | COMMENT ON COLUMN public.account.account_status IS '账户状态'; 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Mac 27 | .DS_Store 28 | 29 | # IDEA 30 | .idea 31 | 32 | # Glide 33 | vendor 34 | 35 | #bench gen 36 | */*.profile 37 | */*.out 38 | */intarray.txt 39 | */*.svg 40 | coverage.txt 41 | tmp 42 | bin 43 | */tmp 44 | deployments/bin 45 | vendor 46 | check.log 47 | *.pid 48 | 49 | */myvariables.sh 50 | 51 | # 编译目录 52 | dist -------------------------------------------------------------------------------- /cinit/mertrics.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/xiaomeng79/istio-micro/internal/metrics" 7 | ) 8 | 9 | func metricsInit(sn string) { 10 | if Config.Metrics.Enable == "yes" { 11 | // Push模式 12 | m := metrics.NewMetrics() 13 | // e.Use(api.MetricsFunc(m)) 14 | m.MemStats() 15 | // InfluxDB 16 | m.InfluxDBWithTags( 17 | time.Duration(Config.Metrics.Duration)*time.Second, 18 | Config.Metrics.URL, 19 | Config.Metrics.Database, 20 | Config.Metrics.UserName, 21 | Config.Metrics.Password, 22 | map[string]string{"service": sn}, 23 | ) 24 | 25 | // Graphite 26 | // addr, _ := net.ResolveTCPAddr("tcp", Conf.Metrics.Address) 27 | // m.Graphite(Conf.Metrics.FreqSec*time.Second, "echo-web.node."+hostname, addr) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cinit/postgres.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/jmoiron/sqlx" 7 | _ "github.com/lib/pq" // postgres驱动 8 | ) 9 | 10 | var Pg *sqlx.DB 11 | 12 | // 初始化连接 13 | func pgInit() { 14 | var err error 15 | dataSourceName := "postgres://" + Config.Postgres.User + ":" + Config.Postgres.Password + "@" + Config.Postgres.Addr + ":" + strconv.Itoa(Config.Postgres.Port) + 16 | "/" + Config.Postgres.DbName + "?sslmode=disable" 17 | Pg, err = sqlx.Open("postgres", dataSourceName) 18 | if err != nil { 19 | panic(err) 20 | } 21 | Pg.SetMaxIdleConns(Config.Postgres.IDleConn) 22 | Pg.SetMaxOpenConns(Config.Postgres.MaxConn) 23 | err = Pg.Ping() 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | 29 | // 关闭 30 | func pgClose() { 31 | Pg.Close() 32 | } 33 | -------------------------------------------------------------------------------- /cinit/mysql.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "strconv" 5 | 6 | _ "github.com/go-sql-driver/mysql" // mysql驱动 7 | "github.com/jmoiron/sqlx" 8 | ) 9 | 10 | var Mysql *sqlx.DB 11 | 12 | // 初始化连接 13 | func mysqlInit() { 14 | var err error 15 | dataSourceName := Config.Mysql.User + ":" + Config.Mysql.Password + "@tcp(" + Config.Mysql.Addr + ":" + strconv.Itoa(Config.Mysql.Port) + 16 | ")/" + Config.Mysql.DbName + "?parseTime=true&loc=Local" 17 | Mysql, err = sqlx.Open("mysql", dataSourceName) 18 | if err != nil { 19 | panic(err) 20 | } 21 | Mysql.SetMaxIdleConns(Config.Mysql.IDleConn) 22 | Mysql.SetMaxOpenConns(Config.Mysql.MaxConn) 23 | err = Mysql.Ping() 24 | if err != nil { 25 | panic(err) 26 | } 27 | } 28 | 29 | // 关闭 30 | func mysqlClose() { 31 | Mysql.Close() 32 | } 33 | -------------------------------------------------------------------------------- /scripts/VARIABLES..md: -------------------------------------------------------------------------------- 1 | ## 安装环境变量配置 2 | 3 | **可在scripts下新建安装环境变量配置文件(myvariables.sh)** 4 | ```bash 5 | #项目相关的 6 | ProjectName=${ProjectName:-"github.com/xiaomeng79/istio-micro"} 7 | Version=${Version:-"unknow"} 8 | TARGET=${TARGET:-'main'} 9 | 10 | #执行环境 11 | GOPROXY=${GOPROXY:-"https://goproxy.cn"} 12 | #go mod是否开启 13 | GO111MODULE=${GO111MODULE:-"auto"} 14 | #GOPATH的路径 15 | GOPATH=${GOPATH:-${HOME}"/com_go"} 16 | #其他软件的安装目录 17 | soft_dir=${soft_dir:-${HOME}} 18 | #go安装的版本 19 | go_version=${go_version:-"1.13.1"} 20 | #protoc的版本 21 | protoc_version=${protoc_version:-"3.6.1"} 22 | #protoc引用的路径 23 | protoc_include_path=${protoc_include_path:-"${soft_dir}/protoc-${protoc_version}-linux-x86_64/include"} 24 | #cloc版本 25 | cloc_version=${cloc_version:-"1.76"} 26 | #执行文件路径 27 | cmd_path=${cmd_path:-"${GOPATH}/bin"} 28 | ``` -------------------------------------------------------------------------------- /scripts/pprof.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | source scripts/.variables.sh 6 | 7 | pidfile=server.pid 8 | #build 9 | pprofon() { 10 | f=deployments/bin/$1_$2/$pidfile 11 | p=`cat $f` 12 | echo "进程号:" $p 13 | kill -10 $p 14 | } 15 | 16 | pprofoff() { 17 | f=deployments/bin/$1_$2/$pidfile 18 | p=`cat $f` 19 | echo "进程号:" $p 20 | kill -12 $p 21 | } 22 | 23 | #判断如何build 24 | case $1 in 25 | pprofon) echo "pprof on:"$2,$3 26 | if [ -z $2 -o -z $3 ];then 27 | echo "参数错误" 28 | exit 2 29 | fi 30 | pprofon $2 $3 31 | ;; 32 | pprofoff) echo "pprof off:"$2,$3 33 | if [ -z $2 -o -z $3 ];then 34 | echo "参数错误" 35 | exit 2 36 | fi 37 | pprofoff $2 $3 38 | ;; 39 | *) 40 | echo "pprof error" 41 | exit 2 42 | ;; 43 | esac 44 | -------------------------------------------------------------------------------- /srv/user/common_cache.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "math/rand" 6 | "strconv" 7 | "time" 8 | 9 | "github.com/xiaomeng79/istio-micro/cinit" 10 | 11 | "github.com/xiaomeng79/go-log" 12 | ) 13 | 14 | const ( 15 | KeyMaxExpire = 500 // 秒 16 | AgainGetStopTime = 100 * time.Millisecond 17 | ) 18 | 19 | func getIDKey(prefix string, ids ...int64) string { 20 | s := prefix 21 | for _, id := range ids { 22 | s += "_" + strconv.FormatInt(id, 10) 23 | } 24 | return s 25 | } 26 | 27 | func setKeyExpire(ctx context.Context, ks ...string) { 28 | rand.Seed(time.Now().UnixNano()) 29 | t := time.Second * time.Duration(rand.Intn(KeyMaxExpire)) 30 | for _, k := range ks { 31 | err := cinit.RedisCli.Expire(k, t).Err() 32 | if err != nil { 33 | log.Error(err.Error(), ctx) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /deployments/config/postgres/pg_hba.conf: -------------------------------------------------------------------------------- 1 | # TYPE DATABASE USER ADDRESS METHOD 2 | 3 | # "local" is for Unix domain socket connections only 4 | local all all trust 5 | # IPv4 local connections: 6 | host all all 127.0.0.1/32 trust 7 | # IPv6 local connections: 8 | host all all ::1/128 trust 9 | # Allow replication connections from localhost, by a user with the 10 | # replication privilege. 11 | local replication all trust 12 | host replication all 127.0.0.1/32 trust 13 | host replication all ::1/128 trust 14 | 15 | host all all all md5 16 | host replication test_rep all md5 17 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - id: "srv-account" 3 | binary: srv-account 4 | main: ./cmd/srv/account.go 5 | goos: 6 | # - windows 7 | - darwin 8 | - linux 9 | - freebsd 10 | goarch: 11 | - amd64 12 | - 386 13 | # - arm 14 | # - arm64 15 | goarm: 16 | # - 6 17 | # - 7 18 | - id: "srv-user" 19 | binary: srv-user 20 | main: ./cmd/srv/user.go 21 | goos: 22 | - linux 23 | - freebsd 24 | goarch: 25 | - amd64 26 | - 386 27 | goarm: 28 | archives: 29 | - id: default 30 | name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' 31 | replacements: 32 | darwin: macOS 33 | # windows: Windows 34 | # 386: i386 35 | # amd64: x86_64 36 | format_overrides: 37 | - goos: windows 38 | format: zip 39 | checksum: 40 | name_template: "checksums.txt" 41 | algorithm: sha256 -------------------------------------------------------------------------------- /internal/utils/log_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/xiaomeng79/go-log" 8 | "github.com/xiaomeng79/go-log/conf" 9 | "github.com/xiaomeng79/go-log/plugins/zaplog" 10 | ) 11 | 12 | // 初始化日志,可以再这里初始化不同日志引擎的日志 、、 zap logrous// 初始化zap// 设置日志引擎为刚初始化的 13 | func logInit() { 14 | log.SetLogger(zaplog.New( 15 | conf.WithIsStdOut("no"), 16 | )) 17 | } 18 | 19 | func BenchmarkLog(b *testing.B) { 20 | b.StopTimer() 21 | logInit() 22 | err := errors.New("my error is test") 23 | b.ReportAllocs() 24 | b.StartTimer() 25 | for i := 0; i < b.N; i++ { 26 | log.Info("test" + err.Error()) 27 | } 28 | } 29 | 30 | func BenchmarkLogF(b *testing.B) { 31 | b.StopTimer() 32 | logInit() 33 | err := errors.New("my error is test2") 34 | b.ReportAllocs() 35 | b.StartTimer() 36 | for i := 0; i < b.N; i++ { 37 | log.Infof("test:%s", err) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /internal/wrapper/logging.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/xiaomeng79/go-log" 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | func LoggingUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 11 | log.Infof("gRPC method: %s, req: %v", info.FullMethod, req, ctx) 12 | resp, err := handler(ctx, req) 13 | log.Infof("gRPC method: %s, resp: %v", info.FullMethod, resp, ctx) 14 | return resp, err 15 | } 16 | 17 | func LoggingUnaryClientInterceptor() grpc.UnaryClientInterceptor { 18 | return func(parentCtx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 19 | err := invoker(parentCtx, method, req, reply, cc, opts...) 20 | log.Infof("method: %s,req: %+v, resp: %+v", method, req, reply, parentCtx) 21 | return err 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /scripts/docker-compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | source scripts/.variables.sh 6 | 7 | #docker-compose 命令 8 | case $1 in 9 | up) echo "docker-compose up" 10 | cd deployments && sudo docker-compose up --build 11 | ;; 12 | dup) echo "docker-compose dup" 13 | cd deployments && sudo docker-compose up --build -d 14 | ;; 15 | stop) echo "docker-compose stop" 16 | cd deployments && sudo docker-compose stop 17 | ;; 18 | restart) echo "docker-compose restart" 19 | cd deployments && sudo docker-compose restart 20 | ;; 21 | ps) echo "docker-compose ps" 22 | cd deployments && sudo docker-compose ps 23 | ;; 24 | kill) echo "docker-compose kill" 25 | cd deployments && sudo docker-compose kill 26 | ;; 27 | rm) echo "docker-compose rm" 28 | cd deployments && sudo docker-compose rm 29 | ;; 30 | *) echo "docker-compose up" 31 | cd deployments && sudo docker-compose up --build 32 | ;; 33 | esac 34 | -------------------------------------------------------------------------------- /cinit/tracer.go: -------------------------------------------------------------------------------- 1 | package cinit 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/xiaomeng79/istio-micro/internal/trace" 7 | 8 | "github.com/opentracing/opentracing-go" 9 | "github.com/xiaomeng79/go-log" 10 | ) 11 | 12 | var c io.Closer 13 | 14 | // 配置文件 15 | func traceInit() { 16 | // 配置 17 | c = traceingInit(Config.Trace.Address, Config.Trace.ZipkinURL, Config.Trace.LogTraceSpans, Config.Trace.SamplingRate, Config.Service.Name) 18 | log.Infof("初始化traceing:%+v", opentracing.GlobalTracer()) 19 | } 20 | 21 | // 关闭 22 | func tracerClose() { 23 | if c != nil { 24 | c.Close() 25 | } 26 | } 27 | 28 | func traceingInit(jaegerURL, zipkinURL string, logTraceSpans bool, samplingRate float64, servicename string) io.Closer { 29 | cl, err := trace.Configure(servicename, &trace.Options{ 30 | JaegerURL: jaegerURL, 31 | ZipkinURL: zipkinURL, 32 | LogTraceSpans: logTraceSpans, 33 | SamplingRate: samplingRate, 34 | }) 35 | if err != nil { 36 | log.Error(err.Error()) 37 | } 38 | return cl 39 | } 40 | -------------------------------------------------------------------------------- /internal/metrics/prometheus/options.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | ) 6 | 7 | var defaultMetricPath = "/metrics" 8 | 9 | type Option func(c *Options) 10 | 11 | type Options struct { 12 | Registry prometheus.Registerer 13 | MetricsPath string 14 | Namespace string 15 | Subsystem string 16 | } 17 | 18 | func Registry(r prometheus.Registerer) Option { 19 | return func(o *Options) { 20 | o.Registry = r 21 | } 22 | } 23 | 24 | func MetricsPath(v string) Option { 25 | return func(o *Options) { 26 | o.MetricsPath = v 27 | } 28 | } 29 | 30 | func Namespace(v string) Option { 31 | return func(o *Options) { 32 | o.Namespace = v 33 | } 34 | } 35 | 36 | func Subsystem(v string) Option { 37 | return func(o *Options) { 38 | o.Subsystem = v 39 | } 40 | } 41 | 42 | func applyOptions(options ...Option) Options { 43 | opts := Options{ 44 | Registry: prometheus.DefaultRegisterer, 45 | MetricsPath: defaultMetricPath, 46 | } 47 | 48 | for _, option := range options { 49 | option(&opts) 50 | } 51 | 52 | return opts 53 | } 54 | -------------------------------------------------------------------------------- /deployments/k8s/api_backend/network/virtual-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: api-backend 5 | spec: 6 | hosts: 7 | - "*" 8 | gateways: 9 | - user-gateway 10 | http: 11 | - match: 12 | # - uri: 13 | # exact: /login 14 | # - uri: 15 | # exact: /logout 16 | - uri: 17 | prefix: /backend/ 18 | route: 19 | - destination: 20 | host: api-backend 21 | port: 22 | number: 8888 23 | corsPolicy: 24 | allowOrigin: 25 | - "*" 26 | allowMethods: 27 | - POST 28 | - GET 29 | - DELETE 30 | - PUT 31 | - PATCH 32 | - OPTIONS 33 | - HEAD 34 | allowCredentials: false 35 | allowHeaders: 36 | - Content-Type 37 | - Accept 38 | - Authorization 39 | # maxAge: "24h" 40 | # weight: 100 41 | # - destination: 42 | # host: api-statis 43 | # subset: v2 44 | # port: 45 | # number: 8888 46 | # weight: 50 47 | 48 | 49 | -------------------------------------------------------------------------------- /srv/user/proto/protoc-gen-swagger/options/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 3 | 4 | package(default_visibility = ["//visibility:public"]) 5 | 6 | filegroup( 7 | name = "options_proto_files", 8 | srcs = [ 9 | "annotations.proto", 10 | "openapiv2.proto", 11 | ], 12 | ) 13 | 14 | go_library( 15 | name = "go_default_library", 16 | embed = [":options_go_proto"], 17 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 18 | ) 19 | 20 | proto_library( 21 | name = "options_proto", 22 | srcs = [ 23 | "annotations.proto", 24 | "openapiv2.proto", 25 | ], 26 | deps = [ 27 | "@com_google_protobuf//:any_proto", 28 | "@com_google_protobuf//:descriptor_proto", 29 | ], 30 | ) 31 | 32 | go_proto_library( 33 | name = "options_go_proto", 34 | compilers = ["@io_bazel_rules_go//proto:go_grpc"], 35 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 36 | proto = ":options_proto", 37 | ) 38 | -------------------------------------------------------------------------------- /srv/account/proto/protoc-gen-swagger/options/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@io_bazel_rules_go//go:def.bzl", "go_library") 2 | load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library") 3 | 4 | package(default_visibility = ["//visibility:public"]) 5 | 6 | filegroup( 7 | name = "options_proto_files", 8 | srcs = [ 9 | "annotations.proto", 10 | "openapiv2.proto", 11 | ], 12 | ) 13 | 14 | go_library( 15 | name = "go_default_library", 16 | embed = [":options_go_proto"], 17 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 18 | ) 19 | 20 | proto_library( 21 | name = "options_proto", 22 | srcs = [ 23 | "annotations.proto", 24 | "openapiv2.proto", 25 | ], 26 | deps = [ 27 | "@com_google_protobuf//:any_proto", 28 | "@com_google_protobuf//:descriptor_proto", 29 | ], 30 | ) 31 | 32 | go_proto_library( 33 | name = "options_go_proto", 34 | compilers = ["@io_bazel_rules_go//proto:go_grpc"], 35 | importpath = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", 36 | proto = ":options_proto", 37 | ) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 meng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /internal/kafka/producer.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/Shopify/sarama" 8 | ) 9 | 10 | type successFunc func(msg *sarama.ProducerMessage) 11 | type errorFunc func(msg *sarama.ProducerError) 12 | 13 | // 同步消息模式 14 | func (k *Kafka) SyncProducer(msg *sarama.ProducerMessage) (part int32, offset int64, err error) { 15 | part, offset, err = k.sp.SendMessage(msg) 16 | return 17 | } 18 | 19 | func (k *Kafka) AsyncProducer(ctx context.Context, msg chan *sarama.ProducerMessage, sf successFunc, ef errorFunc) { 20 | var ( 21 | wg sync.WaitGroup 22 | ) 23 | 24 | wg.Add(1) 25 | go func() { 26 | defer wg.Done() 27 | for msg := range k.ap.Successes() { 28 | sf(msg) 29 | } 30 | }() 31 | 32 | wg.Add(1) 33 | go func() { 34 | defer wg.Done() 35 | for err := range k.ap.Errors() { 36 | ef(err) 37 | } 38 | }() 39 | 40 | ProducerLoop: 41 | for { 42 | select { 43 | case message := <-msg: 44 | k.ap.Input() <- message 45 | 46 | case <-ctx.Done(): 47 | k.ap.AsyncClose() // Trigger a shutdown of the producer. 48 | break ProducerLoop 49 | } 50 | } 51 | 52 | wg.Wait() 53 | } 54 | -------------------------------------------------------------------------------- /srv/account/proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. 2 | 3 | syntax = "proto3"; 4 | 5 | package google.api; 6 | 7 | import "google/api/http.proto"; 8 | import "google/protobuf/descriptor.proto"; 9 | 10 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 11 | option java_multiple_files = true; 12 | option java_outer_classname = "AnnotationsProto"; 13 | option java_package = "com.google.api"; 14 | option objc_class_prefix = "GAPI"; 15 | 16 | extend google.protobuf.MethodOptions { 17 | // See `HttpRule`. 18 | HttpRule http = 72295728; 19 | } 20 | -------------------------------------------------------------------------------- /srv/user/proto/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. 2 | 3 | syntax = "proto3"; 4 | 5 | package google.api; 6 | 7 | import "google/api/http.proto"; 8 | import "google/protobuf/descriptor.proto"; 9 | 10 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 11 | option java_multiple_files = true; 12 | option java_outer_classname = "AnnotationsProto"; 13 | option java_package = "com.google.api"; 14 | option objc_class_prefix = "GAPI"; 15 | 16 | extend google.protobuf.MethodOptions { 17 | // See `HttpRule`. 18 | HttpRule http = 72295728; 19 | } 20 | -------------------------------------------------------------------------------- /api/frontend/user.go: -------------------------------------------------------------------------------- 1 | package frontend 2 | 3 | import ( 4 | "github.com/xiaomeng79/istio-micro/internal/api" 5 | "github.com/xiaomeng79/istio-micro/internal/utils" 6 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 7 | 8 | "github.com/labstack/echo" 9 | "github.com/xiaomeng79/go-log" 10 | ) 11 | 12 | // 查询全部 13 | func UserQueryAll(c echo.Context) error { 14 | ctx := c.Request().Context() 15 | 16 | pageIndex, err := utils.S2N(c.QueryParam("page_index")) 17 | if err != nil { 18 | log.Error(err.Error(), ctx) 19 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 20 | } 21 | pageSize, err := utils.S2N(c.QueryParam("page_size")) 22 | if err != nil { 23 | log.Error(err.Error(), ctx) 24 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 25 | } 26 | 27 | _page := new(pb.Page) 28 | _page.PageSize = pageSize 29 | _page.PageIndex = pageIndex 30 | 31 | _req := new(pb.UserAllOption) 32 | _req.Page = _page 33 | _rsp, err := UserClient.UserQueryAll(ctx, _req) 34 | if err != nil { 35 | // 解析返回的错误信息 36 | log.Error(err.Error(), ctx) 37 | return api.RPCErr(c, err) 38 | } 39 | return api.HandleSuccess(c, _rsp.All, _rsp.Page) 40 | } 41 | -------------------------------------------------------------------------------- /internal/utils/page.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "math" 4 | 5 | // 分页 6 | type Page struct { 7 | PageIndex int64 `json:"page_index"` // 页面索引 8 | PageSize int64 `json:"page_size"` // 每页大小 9 | PageTotal int64 `json:"page_total"` // 总分页数 10 | Count int64 `json:"count"` // 当页记录数 11 | Total int64 `json:"total"` // 总记录数 12 | } 13 | 14 | const ( 15 | PageIndexDefault = 1 16 | PageSizeDefault = 20 17 | ) 18 | 19 | /** 20 | 初始化分页 21 | */ 22 | func (p *Page) InitPage(total int64) { 23 | if p.PageIndex <= 0 { 24 | p.PageIndex = PageIndexDefault 25 | } 26 | if p.PageSize <= 0 { 27 | p.PageSize = PageSizeDefault 28 | } 29 | // 最大的索引 30 | maxIndex := int64(math.Ceil(float64(total) / float64(p.PageSize))) 31 | if maxIndex != 0 && maxIndex < p.PageIndex { 32 | p.PageIndex = maxIndex 33 | } 34 | // 总记录数 35 | p.Total = total 36 | // 总分数页 37 | if maxIndex <= 0 { // 没数据情况 38 | p.PageTotal = 0 39 | p.Count = 0 40 | } else { 41 | p.PageTotal = maxIndex 42 | } 43 | // 当页记录数 44 | if p.PageTotal > p.PageIndex { // 总页数大于当前页数 45 | p.Count = p.PageSize 46 | } 47 | if p.PageTotal == p.PageIndex { // 总页数等于当前页数 48 | p.Count = p.Total - (p.PageSize * (p.PageIndex - 1)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /srv/user/cache.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/xiaomeng79/istio-micro/cinit" 7 | "github.com/xiaomeng79/istio-micro/internal/utils" 8 | 9 | "github.com/xiaomeng79/go-log" 10 | ) 11 | 12 | const ( 13 | CacheIDPrefix = "ucid" 14 | ) 15 | 16 | func CacheGet(ctx context.Context, id int64) (map[string]string, error) { 17 | k := getIDKey(CacheIDPrefix, id) 18 | // 获取全部 19 | r, err := cinit.RedisCli.HGetAll(k).Result() 20 | if err != nil { 21 | log.Info(err.Error(), ctx) 22 | } 23 | if len(r) == 0 { 24 | CacheSet(ctx, id) 25 | } 26 | return r, err 27 | } 28 | 29 | func CacheSet(ctx context.Context, id int64) { 30 | m := new(User) 31 | m.ID = id 32 | err := m.QueryOne(ctx) 33 | if err != nil { 34 | log.Info(err.Error(), ctx) 35 | return 36 | } 37 | _m := utils.Struct2Map(*m) 38 | k := getIDKey(CacheIDPrefix, id) 39 | err = cinit.RedisCli.HMSet(k, _m).Err() 40 | if err != nil { 41 | log.Error(err.Error(), ctx) 42 | return 43 | } 44 | setKeyExpire(ctx, k) 45 | } 46 | 47 | func CacheDel(ctx context.Context, id int64) { 48 | k := getIDKey(CacheIDPrefix, id) 49 | err := cinit.RedisCli.Del(k).Err() 50 | if err != nil { 51 | log.Info(err.Error(), ctx) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/utils/time.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | ) 7 | 8 | // 生成毫秒时间戳字符串 9 | func GenMicTimeStr() string { 10 | return strconv.FormatInt(time.Now().UnixNano()/1e6, 10) 11 | } 12 | 13 | // 毫秒时间戳转字符串 14 | func MicTimeToFormatStr() string { 15 | tm := time.Now() 16 | return tm.Format("2006-01-02 15:04:05") 17 | } 18 | 19 | // 生成毫秒时间戳 20 | func GenMicTime() int64 { 21 | return time.Now().UnixNano() / 1e6 22 | } 23 | 24 | // 毫秒时间戳转字符串 25 | func MicTimeToStr(i64 int64) string { 26 | tm := time.Unix(i64/1e3, 0) 27 | return tm.Format("2006-01-02 15:04:05") 28 | } 29 | 30 | // 毫秒时间戳转字符串 31 | func StrMicTimeToStr(s string) (string, error) { 32 | i64, err := strconv.ParseInt(s, 10, 64) 33 | if err != nil { 34 | return "", err 35 | } 36 | tm := time.Unix(i64/1e3, 0) 37 | return tm.Format("2006-01-02 15:04:05"), nil 38 | } 39 | 40 | // 字符串转毫秒时间戳 41 | func StrToMicTime(s string) string { 42 | // 获取本地location // 待转化为时间戳的字符串 注意 这里的小时和分钟还要秒必须写 因为是跟着模板走的 修改模板的话也可以不写 43 | timeLayout := "2006-01-02 15:04:05" // 转化所需模板 44 | loc, _ := time.LoadLocation("Local") // 重要:获取时区 45 | theTime, _ := time.ParseInLocation(timeLayout, s, loc) // 使用模板在对应时区转化为time.time类型 46 | return strconv.FormatInt(theTime.Unix()*1e3, 10) 47 | } 48 | -------------------------------------------------------------------------------- /scripts/INSTALL.md: -------------------------------------------------------------------------------- 1 | ## 手动安装 2 | 3 | 1. 安装主要程序 4 | - [go](https://studygolang.com/dl) >= 1.13.1 go 5 | - [cloc](https://github.com/AlDanial/cloc) >=1.76 代码统计 6 | - [protoc](https://github.com/protocolbuffers/protobuf) >= 3.6.1 proto buffer 7 | 8 | **注意:安装完protoc后,需要将protoc下的include目录配置到环境变量,如下** 9 | 10 | ```bash 11 | echo protoc_include_path=你的protoc地址 >> ~/.profile 12 | ``` 13 | 14 | 2. 定义GOPATH并设置go的环境变量 15 | ```bash 16 | #新建GOPATH并切换到GOPATH 17 | cd ${GOPATH} 18 | export GOPATH=你的GOPATH路径 19 | #如果不能访问外网设置代理 20 | export GOPROXY=https://goproxy.io 21 | #关闭go mod (这样可以将文件安装到GOPATH) 22 | export GO111MODULE=auto 23 | #将GOPATH下的bin目录放到PATH环境变量下 24 | export PATH=$GOPATH/bin:$PATH 25 | #将以上环境变量配置到~/.profile并执行 26 | source ~/.profile 27 | 28 | ``` 29 | 30 | 3. 安装go的依赖包 31 | ```bash 32 | go get github.com/golangci/golangci-lint/cmd/golangci-lint 33 | go get github.com/golang/protobuf/protoc-gen-go 34 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway 35 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger 36 | go get github.com/favadi/protoc-go-inject-tag 37 | go get github.com/fzipp/gocyclo 38 | go get github.com/rakyll/statik 39 | ``` 40 | 4. 切换到istio-micro目录执行 41 | ```bash 42 | #编译全部 43 | make allbuild 44 | #启动 45 | make compose 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /pkg/pprof/pprof.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | _ "net/http/pprof" // pprof 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "syscall" 11 | ) 12 | 13 | const ( 14 | Addr = ":38888" 15 | PidFile = "server.pid" 16 | ) 17 | 18 | func Run() { 19 | ch := make(chan os.Signal, 1) 20 | ch1 := make(chan os.Signal, 1) 21 | signal.Notify(ch, syscall.SIGUSR1) // 开启pprof 22 | signal.Notify(ch1, syscall.SIGUSR2) // 关闭pprof 23 | 24 | // 将pid写入文件 25 | f, err := os.Create(PidFile) 26 | if err != nil { 27 | log.Printf("%v", err) 28 | } 29 | _, err = f.WriteString(strconv.Itoa(os.Getpid())) 30 | if err != nil { 31 | log.Printf("%v", err) 32 | } 33 | f.Close() 34 | 35 | log.Print("进程id: ", os.Getpid()) 36 | var server *http.Server 37 | for { 38 | select { 39 | case <-ch: 40 | go func() { 41 | server = &http.Server{ 42 | Addr: Addr, 43 | } 44 | log.Print("listen addr: ", Addr) 45 | 46 | err := server.ListenAndServe() 47 | if err != nil { 48 | log.Printf("listen:%v", err) 49 | } 50 | }() 51 | case <-ch1: 52 | if server != nil { 53 | err := server.Close() 54 | if err != nil { 55 | log.Printf("server close:%v", err) 56 | } else { 57 | server = nil 58 | } 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /scripts/.variables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #set -u 4 | 5 | #获取用户自定义变量(myvariables.sh) 6 | if [ -f "scripts/myvariables.sh" ];then 7 | source scripts/myvariables.sh 8 | fi 9 | #项目相关的 10 | ProjectName=${ProjectName:-"github.com/xiaomeng79/istio-micro"} 11 | Version=${Version:-"unknow"} 12 | TARGET=${TARGET:-'main'} 13 | 14 | #执行环境 15 | GOPROXY=${GOPROXY:-"https://goproxy.cn"} 16 | #go mod是否开启 17 | GO111MODULE=${GO111MODULE:-"auto"} 18 | #GOPATH的路径 19 | GOPATH=${GOPATH:-${HOME}"/com_go"} 20 | #其他软件的安装目录 21 | soft_dir=${soft_dir:-${HOME}} 22 | #go安装的版本 23 | go_version=${go_version:-"1.13.1"} 24 | #protoc的版本 25 | protoc_version=${protoc_version:-"3.6.1"} 26 | #protoc引用的路径 27 | protoc_include_path=${protoc_include_path:-"${soft_dir}/protoc-${protoc_version}-linux-x86_64/include"} 28 | #cloc版本 29 | cloc_version=${cloc_version:-"1.76"} 30 | #执行文件路径 31 | cmd_path=${cmd_path:-"${GOPATH}/bin"} 32 | 33 | mkdir -p ${GOPATH}/bin 34 | mkdir -p ${GOPATH}/src 35 | 36 | #将环境变量存入本地环境配置 37 | echo "GOPROXY=${GOPROXY}" >>${HOME}/.profile 38 | echo "protoc_include_path=${protoc_include_path}" >>${HOME}/.profile 39 | echo "GO111MODULE=${GO111MODULE}" >>${HOME}/.profile 40 | echo "GOPATH=${GOPATH}" >>${HOME}/.profile 41 | echo "PATH=${soft_dir}/go/bin:${GOPATH}/bin:${PATH}" >>${HOME}/.profile 42 | 43 | #手动执行 44 | #source ~/.profile -------------------------------------------------------------------------------- /internal/jwt/jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/dgrijalva/jwt-go" 7 | ) 8 | 9 | var ( 10 | secret = []byte("this is a test secret") 11 | ) 12 | 13 | type Msg struct { 14 | UserID int32 `json:"userid"` 15 | UserName string `json:"username"` 16 | } 17 | 18 | type MyCustomClaims struct { 19 | Msg 20 | jwt.StandardClaims 21 | } 22 | 23 | func Encode(r Msg) (string, error) { 24 | claims := MyCustomClaims{ 25 | r, 26 | jwt.StandardClaims{ 27 | // ExpiresAt: time.Now().Unix(),// 过期时间 28 | ExpiresAt: time.Now().Add(time.Hour * 72).Unix(), // 过 期时间 29 | Issuer: "com.example", // 该JWT的签发者,可选 30 | // Subject:"",// 该JWT所面向的用户 31 | }, 32 | } 33 | 34 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 35 | 36 | return token.SignedString(secret) 37 | } 38 | 39 | func Decode(tokenString string) (Msg, error) { 40 | // Parse the token 41 | token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) { 42 | return secret, nil 43 | }) 44 | if err != nil { 45 | return Msg{}, err 46 | } 47 | // Validate the token and return the custom claims 48 | if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid { 49 | return claims.Msg, nil 50 | } 51 | return Msg{}, err 52 | } 53 | -------------------------------------------------------------------------------- /deployments/k8s/api_frontend/network/virtual-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: api-frontend 5 | spec: 6 | hosts: 7 | - "*" 8 | gateways: 9 | - user-gateway 10 | http: 11 | - match: 12 | # - uri: 13 | # exact: /login 14 | # - uri: 15 | # exact: /logout 16 | # - uri: 17 | # prefix: /backend/ 18 | # route: 19 | # - destination: 20 | # host: api-backend 21 | # port: 22 | # number: 8888 23 | 24 | - match: 25 | - uri: 26 | prefix: /frontend/ 27 | route: 28 | - destination: 29 | host: api-frontend 30 | subset: v1 31 | port: 32 | number: 8889 33 | corsPolicy: 34 | allowOrigin: 35 | - "*" 36 | allowMethods: 37 | - POST 38 | - GET 39 | - DELETE 40 | - PUT 41 | - PATCH 42 | - OPTIONS 43 | - HEAD 44 | allowCredentials: false 45 | allowHeaders: 46 | - Content-Type 47 | - Accept 48 | - Authorization 49 | # maxAge: "24h" 50 | # weight: 100 51 | # - destination: 52 | # host: api-statis 53 | # subset: v2 54 | # port: 55 | # number: 8888 56 | # weight: 50 57 | 58 | 59 | -------------------------------------------------------------------------------- /internal/sqlupdate/sqlupdate_test.go: -------------------------------------------------------------------------------- 1 | package sqlupdate 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | var testData = []struct { 10 | ov, cv string 11 | b bool 12 | }{ 13 | {"0.0.1", "0.2.0", true}, 14 | {"v1.0.2", "v3.0.1", true}, 15 | {"1.0.6", "1.0.10", true}, 16 | {"v1.3.2", "v1.2.4", false}, 17 | {"2.3.4", "2.4", true}, 18 | {"2.3.4", "2.3.2.20180809", false}, 19 | {"2.3.4", "2.3.4.20180809", true}, 20 | } 21 | 22 | func TestSQLUpdate_Decode(t *testing.T) { 23 | s := new(SQLUpdate) 24 | err := s.decode("./testdata/record.json") 25 | assert.NoError(t, err) 26 | assert.Equal(t, "test", s.Project) 27 | assert.Equal(t, "0.0.1", s.Update[0].Version) 28 | // 比较版本号 29 | res1 := s.compareResult("0.0.0", "1.1.1") 30 | assert.Equal(t, 4, len(res1)) 31 | res2 := s.compareResult("0.1.0", "1.1.1") 32 | assert.Equal(t, 3, len(res2)) 33 | assert.Equal(t, "1.0.0", res2[0].Version) 34 | } 35 | 36 | func TestCompare(t *testing.T) { 37 | for _, v := range testData { 38 | assert.Equal(t, v.b, compare(v.ov, v.cv)) 39 | } 40 | } 41 | 42 | func TestSQLUpdate_GetSqls(t *testing.T) { 43 | s := new(SQLUpdate) 44 | str, err := s.GetSqls("./testdata/record.json", "0.0.0", "3.0.2") 45 | assert.NoError(t, err) 46 | assert.Contains(t, str, "COMMENT ON COLUMN public.account.balance IS") 47 | } 48 | -------------------------------------------------------------------------------- /deployments/k8s/api_backend/dev.yaml: -------------------------------------------------------------------------------- 1 | #apiVersion: v1 2 | #kind: ServiceAccount 3 | #metadata: 4 | # name: test-name 5 | # namespace: default 6 | #--- 7 | apiVersion: extensions/v1beta1 8 | kind: Deployment 9 | metadata: 10 | namespace: default 11 | name: api-backend-v1 12 | spec: 13 | replicas: 2 14 | template: 15 | metadata: 16 | labels: 17 | app: api-backend 18 | version: v1 19 | spec: 20 | # serviceAccountName: test-name 21 | # imagePullSecrets: 22 | # - name: registry-secret 23 | containers: 24 | - name: api-backend 25 | env: 26 | - name: CONFIGOR_TRACE_ADDRESS 27 | value: jaeger-agent:6831 28 | - name: CONFIGOR_SRVUSER_ADDRESS 29 | value: srv-user:5001 30 | - name: RAND_NUM 31 | value: num179d94 32 | # command: [ 33 | # "/api_backend", 34 | # ] 35 | image: api-backend:v1.0.1 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8888 39 | 40 | 41 | --- 42 | apiVersion: v1 43 | kind: Service 44 | metadata: 45 | name: api-backend 46 | namespace: dev 47 | labels: 48 | app: api-backend 49 | spec: 50 | ports: 51 | - port: 8888 52 | protocol: TCP 53 | targetPort: 8888 54 | selector: 55 | app: api-backend 56 | # type: LoadBalancer 57 | # type: NodePort -------------------------------------------------------------------------------- /deployments/k8s/api_frontend/dev.yaml: -------------------------------------------------------------------------------- 1 | #apiVersion: v1 2 | #kind: ServiceAccount 3 | #metadata: 4 | # name: test-name 5 | # namespace: default 6 | #--- 7 | apiVersion: extensions/v1beta1 8 | kind: Deployment 9 | metadata: 10 | namespace: default 11 | name: api-frontend-v1 12 | spec: 13 | replicas: 2 14 | template: 15 | metadata: 16 | labels: 17 | app: api-frontend 18 | version: v1 19 | spec: 20 | # serviceAccountName: test-name 21 | # imagePullSecrets: 22 | # - name: registry-secret 23 | containers: 24 | - name: api-frontend 25 | env: 26 | - name: CONFIGOR_TRACE_ADDRESS 27 | value: jaeger-agent:6831 28 | - name: CONFIGOR_SRVUSER_ADDRESS 29 | value: srv-user:5001 30 | - name: RAND_NUM 31 | value: num179d94 32 | # command: [ 33 | # "/api_frontend", 34 | # ] 35 | image: api-frontend:v1.0.1 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8889 39 | 40 | 41 | --- 42 | apiVersion: v1 43 | kind: Service 44 | metadata: 45 | name: api-frontend 46 | namespace: dev 47 | labels: 48 | app: api-frontend 49 | spec: 50 | ports: 51 | - port: 8889 52 | protocol: TCP 53 | targetPort: 8889 54 | selector: 55 | app: api-frontend 56 | # type: LoadBalancer 57 | # type: NodePort -------------------------------------------------------------------------------- /srv/account/account.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | 6 | pb "github.com/xiaomeng79/istio-micro/srv/account/proto" 7 | 8 | "github.com/golang/protobuf/ptypes/empty" 9 | "google.golang.org/grpc/codes" 10 | "google.golang.org/grpc/status" 11 | ) 12 | 13 | type Server struct{} 14 | 15 | func (s *Server) AccountAdd(ctx context.Context, in *pb.AccountBase) (out *pb.AccountBase, outerr error) { 16 | m := new(Account) 17 | m.base = in 18 | out = new(pb.AccountBase) 19 | err := m.Add(ctx) 20 | if err != nil { 21 | outerr = status.Error(codes.InvalidArgument, err.Error()) 22 | return 23 | } 24 | out = m.base 25 | return 26 | } 27 | 28 | func (s *Server) AccountUpdate(ctx context.Context, in *pb.AccountUpdateReq) (out *empty.Empty, outerr error) { 29 | m := &Account{base: &pb.AccountBase{ 30 | Id: in.Id, 31 | Balance: in.Balance, 32 | }} 33 | out = new(empty.Empty) 34 | err := m.Update(ctx) 35 | if err != nil { 36 | outerr = status.Error(codes.InvalidArgument, err.Error()) 37 | return 38 | } 39 | return 40 | } 41 | 42 | // 查询一个 43 | func (s *Server) AccountQueryOne(ctx context.Context, in *pb.AccountID) (out *pb.AccountBase, outerr error) { 44 | m := &Account{ 45 | base: &pb.AccountBase{ 46 | Id: in.Id, 47 | }} 48 | out = new(pb.AccountBase) 49 | err := m.QueryOne(ctx) 50 | if err != nil { 51 | outerr = status.Error(codes.InvalidArgument, err.Error()) 52 | return 53 | } 54 | out = m.base 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /scripts/proto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | source scripts/.variables.sh 6 | 7 | #protoc_include_path=${protoc_include_path:-""} 8 | 9 | proto() { 10 | echo "protoc include 路径为:${protoc_include_path}" 11 | proto_path=`pwd`/pkg/proto 12 | dirname=./srv/$1/proto 13 | swagger_dir=./deployments/config/swagger 14 | if [ ! -d $swagger_dir ];then 15 | mkdir -p $swagger_dir 16 | fi 17 | if [ -d $dirname ];then 18 | for f in $dirname/*.proto; do \ 19 | if [ -f $f ];then \ 20 | protoc -I. \ 21 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 22 | -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway \ 23 | --proto_path=${protoc_include_path} \ 24 | --grpc-gateway_out=. \ 25 | --swagger_out=$swagger_dir \ 26 | --swagger_out=. \ 27 | --go_out=plugins=grpc:. $f; \ 28 | echo compiled protoc: $f; \ 29 | fi \ 30 | done \ 31 | fi 32 | } 33 | 34 | proto_inject() { 35 | echo "开始注入" 36 | dirname=./srv/$1/proto 37 | if [ -d $dirname ];then 38 | for f in $dirname/*.pb.go; do \ 39 | if [ -f $f ];then \ 40 | protoc-go-inject-tag -input=./$f; \ 41 | echo "完成注入" protoc-go-inject-tag: $f; \ 42 | fi \ 43 | done \ 44 | fi 45 | } 46 | 47 | # 用户 48 | proto user 49 | proto_inject user 50 | 51 | # 账户 52 | proto account 53 | proto_inject account 54 | -------------------------------------------------------------------------------- /internal/trace/options.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Options defines the set of options supported by Istio's component tracing package. 8 | type Options struct { 9 | // URL of zipkin collector (example: 'http://zipkin:9411/api/v1/spans'). 10 | ZipkinURL string 11 | 12 | // URL of jaeger HTTP collector (example: 'http://jaeger:14268/api/traces?format=jaeger.thrift'). 13 | JaegerURL string 14 | 15 | // Whether or not to emit trace spans as log records. 16 | LogTraceSpans bool 17 | 18 | // SamplingRate controls the rate at which a process will decide to generate trace spans. 19 | SamplingRate float64 20 | } 21 | 22 | // DefaultOptions returns a new set of options, initialized to the defaults 23 | func DefaultOptions() *Options { 24 | return &Options{} 25 | } 26 | 27 | // Validate returns whether the options have been configured correctly or an error 28 | func (o *Options) Validate() error { 29 | // due to a race condition in the OT libraries somewhere, we can't have both tracing outputs active at once 30 | if o.JaegerURL != "" && o.ZipkinURL != "" { 31 | return errors.New("can't have Jaeger and Zipkin outputs active simultaneously") 32 | } 33 | 34 | if o.SamplingRate > 1.0 || o.SamplingRate < 0.0 { 35 | return errors.New("sampling rate must be in the range: [0.0, 1.0]") 36 | } 37 | 38 | return nil 39 | } 40 | 41 | // TracingEnabled returns whether the given options enable tracing to take place. 42 | func (o *Options) TracingEnabled() bool { 43 | return o.JaegerURL != "" || o.ZipkinURL != "" || o.LogTraceSpans 44 | } 45 | -------------------------------------------------------------------------------- /deployments/k8s/srv_user/dev.yaml: -------------------------------------------------------------------------------- 1 | #apiVersion: v1 2 | #kind: ServiceAccount 3 | #metadata: 4 | # name: test-name 5 | # namespace: default 6 | #--- 7 | apiVersion: extensions/v1beta1 8 | kind: Deployment 9 | metadata: 10 | namespace: default 11 | name: srv-user-v1 12 | spec: 13 | replicas: 2 14 | template: 15 | metadata: 16 | labels: 17 | app: srv-user 18 | version: v1 19 | spec: 20 | # serviceAccountName: test-name 21 | # imagePullSecrets: 22 | # - name: registry-secret 23 | containers: 24 | - name: srv-user 25 | env: 26 | - name: CONFIGOR_TRACE_ADDRESS 27 | value: jaeger-agent:6831 28 | # - name: CONFIGOR_SERVICE_NAME 29 | # value: srv-user 30 | - name: CONFIGOR_MYSQL_DBNAME 31 | value: user 32 | - name: CONFIGOR_MYSQL_ADDR 33 | value: mysqladdr 34 | - name: CONFIGOR_MYSQL_USER 35 | value: test 36 | - name: CONFIGOR_MYSQL_PASSWORD 37 | value: test 38 | - name: RAND_NUM 39 | value: num17994 40 | # command: [ 41 | # "/srv_user", 42 | # ] 43 | image: srv-user:v1.0.1 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 5001 47 | 48 | 49 | --- 50 | apiVersion: v1 51 | kind: Service 52 | metadata: 53 | name: srv-user 54 | namespace: default 55 | labels: 56 | app: srv-user 57 | spec: 58 | ports: 59 | - port: 5001 60 | protocol: TCP 61 | targetPort: 5001 62 | selector: 63 | app: srv-user 64 | # type: LoadBalancer 65 | # type: NodePort -------------------------------------------------------------------------------- /deployments/k8s/srv_socket/dev.yaml: -------------------------------------------------------------------------------- 1 | #apiVersion: v1 2 | #kind: ServiceAccount 3 | #metadata: 4 | # name: test-name 5 | # namespace: default 6 | #--- 7 | apiVersion: extensions/v1beta1 8 | kind: Deployment 9 | metadata: 10 | namespace: default 11 | name: srv-socket-v1 12 | spec: 13 | replicas: 2 14 | template: 15 | metadata: 16 | labels: 17 | app: srv-socket 18 | version: v1 19 | spec: 20 | # serviceAccountName: test-name 21 | # imagePullSecrets: 22 | # - name: registry-secret 23 | containers: 24 | - name: srv-socket 25 | env: 26 | - name: CONFIGOR_TRACE_ADDRESS 27 | value: jaeger-agent:6831 28 | # - name: CONFIGOR_SERVICE_NAME 29 | # value: srv-socket 30 | - name: CONFIGOR_MYSQL_DBNAME 31 | value: socket 32 | - name: CONFIGOR_MYSQL_ADDR 33 | value: mysqladdr 34 | - name: CONFIGOR_MYSQL_socket 35 | value: test 36 | - name: CONFIGOR_MYSQL_PASSWORD 37 | value: test 38 | - name: RAND_NUM 39 | value: num17994 40 | # command: [ 41 | # "/srv_socket", 42 | # ] 43 | image: srv-socket:v1.0.1 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 5002 47 | 48 | 49 | --- 50 | apiVersion: v1 51 | kind: Service 52 | metadata: 53 | name: srv-socket 54 | namespace: default 55 | labels: 56 | app: srv-socket 57 | spec: 58 | ports: 59 | - port: 5002 60 | protocol: TCP 61 | targetPort: 5002 62 | selector: 63 | app: srv-socket 64 | # type: LoadBalancer 65 | # type: NodePort -------------------------------------------------------------------------------- /internal/gateway/gateway.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | 9 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | // 注册 14 | type regHandle func(context.Context, *gwruntime.ServeMux, *grpc.ClientConn) error 15 | 16 | // 新建网关 17 | func newGateway(ctx context.Context, conn *grpc.ClientConn, opts []gwruntime.ServeMuxOption, handles []regHandle) (http.Handler, error) { 18 | mux := gwruntime.NewServeMux(opts...) 19 | 20 | for _, f := range handles { 21 | if err := f(ctx, mux, conn); err != nil { 22 | return nil, err 23 | } 24 | } 25 | return mux, nil 26 | } 27 | 28 | func dial(ctx context.Context, network, addr string) (*grpc.ClientConn, error) { 29 | switch network { 30 | case "tcp": 31 | return dialTCP(ctx, addr) 32 | case "unix": 33 | return dialUnix(ctx, addr) 34 | default: 35 | return nil, fmt.Errorf("unsupported network type %q", network) 36 | } 37 | } 38 | 39 | // dialTCP creates a client connection via TCP.// "addr" must be a valid TCP address with a port number. 40 | func dialTCP(ctx context.Context, addr string) (*grpc.ClientConn, error) { 41 | return grpc.DialContext(ctx, addr, grpc.WithInsecure()) 42 | } 43 | 44 | // dialUnix creates a client connection via a unix domain socket.// "addr" must be a valid path to the socket. 45 | func dialUnix(ctx context.Context, addr string) (*grpc.ClientConn, error) { 46 | d := func(ctx context.Context, addr string) (net.Conn, error) { 47 | return net.Dial("unix", addr) 48 | } 49 | return grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithContextDialer(d)) 50 | } 51 | -------------------------------------------------------------------------------- /scripts/dockerfile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu 4 | 5 | source scripts/.variables.sh 6 | 7 | #生成dockerfile 8 | gen(){ 9 | #程序名称 10 | pname="$1_$2" 11 | #模板 12 | filename=./deployments/bin/"$pname" 13 | swagger=./deployments/config/swagger/$1/$2/proto 14 | #判断bin是否存在 15 | if [ ! -d deployments/bin/"$pname" ];then 16 | mkdir -p deployments/bin/"$pname"/proto 17 | fi 18 | 19 | #判断swagger存在复制到这个目录下面 20 | if [ -d $swagger ];then 21 | cp -r $swagger/ $filename 22 | fi 23 | 24 | #添加本地时区 25 | #RUN set -xe && apk add --no-cache tzdata && cp -r -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 26 | 27 | cat>$filename/Dockerfile< /etc/apk/repositories \ 30 | && echo 'http://mirrors.ustc.edu.cn/alpine/v3.5/community' >>/etc/apk/repositories \ 31 | && apk update && apk add tzdata \ 32 | && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ 33 | && echo "Asia/Shanghai" > /etc/timezone 34 | 35 | ADD $pname /$pname 36 | ADD proto/ /swagger 37 | ADD sqlupdate/ /sqlupdate 38 | RUN chmod +x /$pname 39 | ENTRYPOINT [ "/$pname" ] 40 | EOF 41 | echo "生成dockerfile $pname" 42 | } 43 | 44 | #全部生成dockerfile 45 | allgen() { 46 | gen api backend 47 | gen api frontend 48 | gen srv user 49 | gen srv socket 50 | gen srv account 51 | } 52 | 53 | #判断如何build 54 | case $1 in 55 | alldf) echo "全部生成dockerfile" 56 | allgen 57 | ;; 58 | df) echo "生成dockerfile:"$2,$3 59 | if [ -z $2 -o -z $3 ];then 60 | echo "参数错误" 61 | exit 2 62 | fi 63 | gen $2 $3 64 | ;; 65 | *) 66 | echo "生成dockerfile error" 67 | exit 2 68 | ;; 69 | esac -------------------------------------------------------------------------------- /internal/kafka/init.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Shopify/sarama" 7 | cluster "github.com/bsm/sarama-cluster" 8 | "github.com/xiaomeng79/go-log" 9 | ) 10 | 11 | type Kafka struct { 12 | addrs []string 13 | c sarama.Client 14 | sp sarama.SyncProducer 15 | ap sarama.AsyncProducer 16 | sc *cluster.Client 17 | ss sarama.Consumer 18 | } 19 | 20 | func NewKafka(addrs []string) *Kafka { 21 | config := sarama.NewConfig() 22 | config.Producer.Return.Successes = true 23 | config.Producer.Timeout = 5 * time.Second 24 | config.Producer.Return.Errors = true 25 | 26 | c, err := sarama.NewClient(addrs, config) 27 | if err != nil { 28 | log.Fatal(err.Error()) 29 | } 30 | cconfig := cluster.NewConfig() 31 | cconfig.Consumer.Return.Errors = true 32 | cconfig.Group.Return.Notifications = true 33 | // cconfig.Group.Mode = cluster.ConsumerModePartitions 34 | sc, err := cluster.NewClient(addrs, cconfig) 35 | if err != nil { 36 | log.Fatal(err.Error()) 37 | } 38 | 39 | sp, err := sarama.NewSyncProducerFromClient(c) 40 | if err != nil { 41 | log.Fatalf("sarama.NewSyncProducer err, message=%s \n", err) 42 | } 43 | ap, err := sarama.NewAsyncProducerFromClient(c) 44 | if err != nil { 45 | log.Fatalf("sarama.NewAsyncProducer err, message=%s \n", err) 46 | } 47 | ss, err := sarama.NewConsumerFromClient(c) 48 | if err != nil { 49 | log.Fatalf("sarama.NewConsumer err, message=%s \n", err) 50 | } 51 | return &Kafka{addrs: addrs, c: c, sc: sc, sp: sp, ap: ap, ss: ss} 52 | } 53 | 54 | func (k *Kafka) Close() { 55 | err := k.c.Close() 56 | if err != nil { 57 | log.Error("kafka close:", err.Error()) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /srv/account/versionupdate.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "github.com/xiaomeng79/istio-micro/cinit" 5 | "github.com/xiaomeng79/istio-micro/internal/sqlupdate" 6 | "github.com/xiaomeng79/istio-micro/version" 7 | 8 | "github.com/xiaomeng79/go-log" 9 | ) 10 | 11 | // 获取旧的版本号 12 | func getOldVersion() (string, error) { 13 | var oldversion string 14 | err := cinit.Pg.Get(&oldversion, `select version from sys_info where id =1 limit 1`) 15 | if err != nil { 16 | log.Errorf("获取旧版本号失败:%+v", err) 17 | return "", err 18 | } 19 | return oldversion, nil 20 | } 21 | 22 | // 更新新的版本号 23 | func updateVersion() error { 24 | _, err := cinit.Pg.Exec(`update sys_info set version=$1 where id=1`, version.Version) 25 | if err != nil { 26 | log.Errorf("更新版本号失败:%+v", err) 27 | return err 28 | } 29 | return nil 30 | } 31 | 32 | // 获取需要执行的sql 33 | func getSQL() (string, error) { 34 | oldVersion, err := getOldVersion() 35 | if err != nil { 36 | return "", err 37 | } 38 | s := new(sqlupdate.SQLUpdate) 39 | sqls, err := s.GetSqls("./sqlupdate/record.json", oldVersion, version.Version) 40 | if err != nil { 41 | log.Errorf("获取执行sql失败:%+v", err) 42 | return "", err 43 | } 44 | return sqls, nil 45 | } 46 | 47 | // 执行sql 48 | func execUpdateSQL() error { 49 | sqls, err := getSQL() 50 | if err != nil { 51 | if err == sqlupdate.ErrNoSQLNeedUpdate { 52 | return nil 53 | } 54 | return err 55 | } 56 | tx, err := cinit.Pg.Begin() 57 | if err != nil { 58 | return err 59 | } 60 | _, err = tx.Exec(sqls) 61 | if err != nil { 62 | log.Errorf("执行sql失败:%+v", err) 63 | _ = tx.Rollback() 64 | return err 65 | } 66 | _ = tx.Commit() 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /srv/user/run.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "github.com/xiaomeng79/istio-micro/cinit" 8 | "github.com/xiaomeng79/istio-micro/internal/gateway" 9 | "github.com/xiaomeng79/istio-micro/internal/wrapper" 10 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 11 | 12 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 13 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 14 | "github.com/xiaomeng79/go-log" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | const ( 20 | SN = "srv-user" // 定义services名称 21 | ) 22 | 23 | func Run() { 24 | // 初始化,选着需要的组件 25 | cinit.InitOption(SN, cinit.Trace, cinit.MySQL, cinit.Redis, cinit.Kafka, cinit.Metrics) 26 | 27 | lis, err := net.Listen("tcp", cinit.Config.SrvUser.Port) 28 | if err != nil { 29 | log.Fatal("failed to listen: " + err.Error()) 30 | } 31 | 32 | s := grpc.NewServer( 33 | grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( 34 | grpc_opentracing.UnaryServerInterceptor(), 35 | wrapper.RecoveryUnaryInterceptor, 36 | wrapper.LoggingUnaryInterceptor, 37 | )), 38 | grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( 39 | grpc_opentracing.StreamServerInterceptor(), 40 | )), 41 | ) 42 | pb.RegisterUserServiceServer(s, &Server{}) 43 | reflection.Register(s) 44 | 45 | // 开启一个网关服务 46 | ctx := context.Background() 47 | go func() { 48 | _ = gateway.Run( 49 | ctx, 50 | gateway.WithAddr(cinit.Config.SrvUser.GateWayAddr), 51 | gateway.WithGRPCServer("tcp", cinit.Config.SrvUser.Address), 52 | gateway.WithSwaggerDir(cinit.Config.SrvUser.GateWaySwaggerDir), 53 | gateway.WithHandle(pb.RegisterUserServiceHandler), 54 | ) 55 | }() 56 | 57 | if err := s.Serve(lis); err != nil { 58 | log.Fatal("failed to listen: " + err.Error()) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/metrics/README.md: -------------------------------------------------------------------------------- 1 | # Metrics 2 | 3 | [来源](https://github.com/hb-go/echo-web/blob/master/middleware/metrics/README.md) 4 | 5 | ## Grafana 6 | 浏览器:localhost:3000 7 | ```bash 8 | docker run --name grafana -d -p 3000:3000 grafana/grafana 9 | ``` 10 | 11 | ## Prometheus 12 | 浏览器:localhost:9090 13 | ```bash 14 | docker run -d --name prometheus -p 9090:9090 -v ~/tmp/prometheus.yml:/etc/prometheus/prometheus.yml \ 15 | prom/prometheus 16 | 17 | # Dashboard JSON 18 | # metrics/prometheus/grafana.json 19 | ``` 20 | 21 | 配置文件deployments/configs/prometheus.yml 22 | ```bash 23 | global: 24 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 25 | evaluation_interval: 15s # Evaluate rules every 15 seconds. 26 | 27 | # Attach these extra labels to all timeseries collected by this Prometheus instance. 28 | external_labels: 29 | monitor: 'codelab-monitor' 30 | 31 | rule_files: 32 | - 'prometheus.rules.yml' 33 | 34 | scrape_configs: 35 | - job_name: 'prometheus' 36 | 37 | # Override the global default and scrape targets from this job every 5 seconds. 38 | scrape_interval: 5s 39 | 40 | static_configs: 41 | - targets: ['localhost:9090'] 42 | 43 | - job_name: 'select_web' 44 | 45 | # Override the global default and scrape targets from this job every 5 seconds. 46 | scrape_interval: 5s 47 | 48 | static_configs: 49 | - targets: ['www.localhost.com'] 50 | labels: 51 | group: 'production' 52 | 53 | ``` 54 | 55 | ## Push模式 56 | ### Graphite 57 | 浏览器:localhost:8090 58 | ```sh 59 | # 登录账户名:root,密码:root 60 | docker run -d --name graphite --restart=always -p 8090:80 -p 2003-2004:2003-2004 -p 2023-2024:2023-2024 -p 8125:8125/udp -p 8126:8126 hopsoft/graphite-statsd 61 | 62 | # Dashboard JSON 63 | # middleware/metrics/grafana_graphite.json 64 | ``` 65 | 66 | ### InfluxDB 67 | ```bash 68 | 69 | ``` 70 | -------------------------------------------------------------------------------- /srv/user/proto/protoc-gen-swagger/options/annotations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.gateway.protoc_gen_swagger.options; 4 | 5 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"; 6 | 7 | import "protoc-gen-swagger/options/openapiv2.proto"; 8 | import "google/protobuf/descriptor.proto"; 9 | 10 | extend google.protobuf.FileOptions { 11 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 12 | // 13 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 14 | // different descriptor messages. 15 | Swagger openapiv2_swagger = 1042; 16 | } 17 | extend google.protobuf.MethodOptions { 18 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 19 | // 20 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 21 | // different descriptor messages. 22 | Operation openapiv2_operation = 1042; 23 | } 24 | extend google.protobuf.MessageOptions { 25 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 26 | // 27 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 28 | // different descriptor messages. 29 | Schema openapiv2_schema = 1042; 30 | } 31 | extend google.protobuf.ServiceOptions { 32 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 33 | // 34 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 35 | // different descriptor messages. 36 | Tag openapiv2_tag = 1042; 37 | } 38 | extend google.protobuf.FieldOptions { 39 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 40 | // 41 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 42 | // different descriptor messages. 43 | JSONSchema openapiv2_field = 1042; 44 | } 45 | -------------------------------------------------------------------------------- /srv/account/proto/protoc-gen-swagger/options/annotations.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package grpc.gateway.protoc_gen_swagger.options; 4 | 5 | option go_package = "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"; 6 | 7 | import "protoc-gen-swagger/options/openapiv2.proto"; 8 | import "google/protobuf/descriptor.proto"; 9 | 10 | extend google.protobuf.FileOptions { 11 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 12 | // 13 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 14 | // different descriptor messages. 15 | Swagger openapiv2_swagger = 1042; 16 | } 17 | extend google.protobuf.MethodOptions { 18 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 19 | // 20 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 21 | // different descriptor messages. 22 | Operation openapiv2_operation = 1042; 23 | } 24 | extend google.protobuf.MessageOptions { 25 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 26 | // 27 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 28 | // different descriptor messages. 29 | Schema openapiv2_schema = 1042; 30 | } 31 | extend google.protobuf.ServiceOptions { 32 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 33 | // 34 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 35 | // different descriptor messages. 36 | Tag openapiv2_tag = 1042; 37 | } 38 | extend google.protobuf.FieldOptions { 39 | // ID assigned by protobuf-global-extension-registry@google.com for grpc-gateway project. 40 | // 41 | // All IDs are the same, as assigned. It is okay that they are the same, as they extend 42 | // different descriptor messages. 43 | JSONSchema openapiv2_field = 1042; 44 | } 45 | -------------------------------------------------------------------------------- /internal/utils/convert.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "strconv" 7 | 8 | "github.com/xiaomeng79/istio-micro/cinit" 9 | 10 | "github.com/mitchellh/mapstructure" 11 | "github.com/xiaomeng79/go-utils/math" 12 | ) 13 | 14 | func S2ID(idS string) (int64, error) { 15 | if idS == "" { 16 | return 0, errors.New("id不能为空") 17 | } 18 | id, err := strconv.ParseInt(idS, 10, 64) 19 | if err != nil { 20 | return 0, err 21 | } 22 | if id <= 0 { 23 | return 0, errors.New("id不能小于0") 24 | } 25 | return id, nil 26 | } 27 | 28 | func S2N(idS string) (int64, error) { 29 | if idS == "" { 30 | return 0, nil 31 | } 32 | num, err := strconv.ParseInt(idS, 10, 64) 33 | if err != nil { 34 | return 0, err 35 | } 36 | return num, nil 37 | } 38 | 39 | func S2F64(idS string) (float64, error) { 40 | if idS == "" { 41 | return 0, nil 42 | } 43 | num, err := strconv.ParseFloat(idS, 64) 44 | if err != nil { 45 | return 0, err 46 | } 47 | return num, nil 48 | } 49 | 50 | func S2I32(idS string) (int32, error) { 51 | if idS == "" { 52 | return 0, nil 53 | } 54 | num, err := strconv.ParseInt(idS, 10, 64) 55 | if err != nil { 56 | return 0, err 57 | } 58 | return int32(num), nil 59 | } 60 | 61 | func Struct2Map(obj interface{}) map[string]interface{} { 62 | t := reflect.TypeOf(obj) 63 | v := reflect.ValueOf(obj) 64 | 65 | var data = make(map[string]interface{}) 66 | for i := 0; i < t.NumField(); i++ { 67 | data[t.Field(i).Name] = v.Field(i).Interface() 68 | } 69 | return data 70 | } 71 | 72 | func OddsCompute(o1, o2 float64) bool { 73 | return math.Round(o1, cinit.FloatComputeBit) == math.Round(o2, cinit.FloatComputeBit) 74 | } 75 | 76 | func Map2Struct(input, result interface{}) { 77 | config := &mapstructure.DecoderConfig{ 78 | WeaklyTypedInput: true, 79 | Result: &result, 80 | } 81 | 82 | decoder, err := mapstructure.NewDecoder(config) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | err = decoder.Decode(input) 88 | if err != nil { 89 | panic(err) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /internal/kafka/consumer.go: -------------------------------------------------------------------------------- 1 | package kafka 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/Shopify/sarama" 7 | cluster "github.com/bsm/sarama-cluster" 8 | "github.com/xiaomeng79/go-log" 9 | ) 10 | 11 | type rmsgFunc func(*sarama.ConsumerMessage) 12 | type rerrFunc func(*sarama.ConsumerError) // type errFunc func(error) 13 | 14 | func (k *Kafka) ConsumerGroup(ctx context.Context, gid string, topics []string, rf rmsgFunc) { 15 | // topics := []string{"test3", "my_topic"} 16 | consumer, err := cluster.NewConsumerFromClient(k.sc, gid, topics) 17 | if err != nil { 18 | panic(err) 19 | } 20 | defer consumer.Close() 21 | 22 | // consume errors 23 | go func() { 24 | for err := range consumer.Errors() { 25 | log.Error("kafka consumer Error:" + err.Error()) 26 | } 27 | }() 28 | 29 | // consume notifications 30 | go func() { 31 | for ntf := range consumer.Notifications() { 32 | log.Infof("Rebalanced: %+v\n", ntf) 33 | } 34 | }() 35 | 36 | // consume messages, watch signals 37 | for { 38 | select { 39 | case msg, ok := <-consumer.Messages(): 40 | if ok { 41 | rf(msg) 42 | // fmt.Fprintf(os.Stdout, "%s/%d/%d\t%s\t%s\n", msg.Topic, msg.Partition, msg.Offset, msg.Key, msg.Value) 43 | consumer.MarkOffset(msg, "") // mark message as processed 44 | } 45 | case <-ctx.Done(): 46 | return 47 | } 48 | } 49 | } 50 | 51 | func (k *Kafka) Consumer(ctx context.Context, topic string, partition int32, offset int64, rf rmsgFunc, ef rerrFunc) { 52 | partitionConsumer, err := k.ss.ConsumePartition(topic, partition, offset) 53 | if err != nil { 54 | log.Error("kafka consumer Error:" + err.Error()) 55 | } 56 | // defer func() { 57 | // if err := partitionConsumer.Close(); err != nil { 58 | // log.Error("kafka consumer Error:"+err.Error()) 59 | // } 60 | // }() 61 | 62 | ConsumerLoop: 63 | for { 64 | select { 65 | case err := <-partitionConsumer.Errors(): 66 | ef(err) 67 | case msg := <-partitionConsumer.Messages(): 68 | rf(msg) 69 | case <-ctx.Done(): 70 | break ConsumerLoop 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /internal/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | "github.com/rcrowley/go-metrics" 8 | influxdb "github.com/vrischmann/go-metrics-influxdb" 9 | ) 10 | 11 | type Metrics struct { 12 | opts Options 13 | } 14 | 15 | // NewMetrics creates a new Metrics 16 | func NewMetrics(options ...Option) *Metrics { 17 | opts := applyOptions(options...) 18 | return &Metrics{opts: opts} 19 | } 20 | 21 | func (m *Metrics) WithPrefix(s string) string { 22 | return m.opts.Prefix + "." + s 23 | } 24 | 25 | func (m *Metrics) GetRegistry() metrics.Registry { 26 | return m.opts.Registry 27 | } 28 | 29 | func (m *Metrics) MemStats() { 30 | metrics.RegisterRuntimeMemStats(m.opts.Registry) 31 | go metrics.CaptureRuntimeMemStats(m.opts.Registry, 5*time.Second) 32 | } 33 | 34 | // Log reports metrics into logs.// // m.Log(30*time.Second, e.Logger)// 35 | func (m *Metrics) Log(freq time.Duration, l metrics.Logger) { 36 | go metrics.Log(m.opts.Registry, freq, l) 37 | } 38 | 39 | // Graphite reports metrics into graphite.// // addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:2003")// m.Graphite(10e9, "metrics", addr)// 40 | func (m *Metrics) Graphite(freq time.Duration, prefix string, addr *net.TCPAddr) { 41 | go metrics.Graphite(m.opts.Registry, freq, prefix, addr) 42 | } 43 | 44 | // InfluxDB reports metrics into influxdb.// // m.InfluxDB(10e9, "http://127.0.0.1:8086","metrics", "test","test"})// 45 | func (m *Metrics) InfluxDB(freq time.Duration, url, database, username, password string) { 46 | go influxdb.InfluxDB(m.opts.Registry, freq, url, database, username, password) 47 | } 48 | 49 | // InfluxDBWithTags reports metrics into influxdb with tags.// you can set node info into tags.// // m.InfluxDBWithTags(10e9, "http://127.0.0.1:8086","metrics", "test","test", map[string]string{"host":"127.0.0.1"})// 50 | func (m *Metrics) InfluxDBWithTags(freq time.Duration, url, database, username, password string, tags map[string]string) { 51 | go influxdb.InfluxDBWithTags(m.opts.Registry, freq, url, database, username, password, tags) 52 | } 53 | -------------------------------------------------------------------------------- /srv/account/run.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "github.com/xiaomeng79/istio-micro/cinit" 8 | "github.com/xiaomeng79/istio-micro/internal/gateway" 9 | "github.com/xiaomeng79/istio-micro/internal/wrapper" 10 | pb "github.com/xiaomeng79/istio-micro/srv/account/proto" 11 | 12 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 13 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 14 | "github.com/xiaomeng79/go-log" 15 | "google.golang.org/grpc" 16 | "google.golang.org/grpc/reflection" 17 | ) 18 | 19 | const ( 20 | SN = "srv-account" // 定义services名称 21 | ) 22 | 23 | func Run() { 24 | // 初始化,选着需要的组件 25 | cinit.InitOption(SN, cinit.Trace, cinit.Postgres, cinit.Metrics) 26 | // 更新sql 27 | err := execUpdateSQL() 28 | if err != nil { 29 | log.Fatalf("updatesql error:%+v ", err) 30 | } 31 | // 更新版本号 32 | err = updateVersion() 33 | if err != nil { 34 | log.Fatalf("update version error:%+v ", err) 35 | } 36 | lis, err := net.Listen("tcp", cinit.Config.SrvAccount.Port) 37 | if err != nil { 38 | log.Fatal("failed to listen: " + err.Error()) 39 | } 40 | 41 | s := grpc.NewServer( 42 | grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( 43 | grpc_opentracing.UnaryServerInterceptor(), 44 | wrapper.RecoveryUnaryInterceptor, 45 | wrapper.LoggingUnaryInterceptor, 46 | )), 47 | grpc.StreamInterceptor(grpc_middleware.ChainStreamServer( 48 | grpc_opentracing.StreamServerInterceptor(), 49 | )), 50 | ) 51 | pb.RegisterAccountServiceServer(s, &Server{}) 52 | reflection.Register(s) 53 | 54 | // 开启一个网关服务 55 | ctx := context.Background() 56 | go func() { 57 | _ = gateway.Run( 58 | ctx, 59 | gateway.WithAddr(cinit.Config.SrvAccount.GateWayAddr), 60 | gateway.WithGRPCServer("tcp", cinit.Config.SrvAccount.Address), 61 | gateway.WithSwaggerDir(cinit.Config.SrvAccount.GateWaySwaggerDir), 62 | gateway.WithHandle(pb.RegisterAccountServiceHandler), 63 | ) 64 | }() 65 | 66 | if err := s.Serve(lis); err != nil { 67 | log.Fatal("failed to listen: " + err.Error()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /deployments/config/mysql/user.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.22, for Linux (x86_64) 2 | -- 3 | -- Host: 127.0.0.1 Database: user 4 | -- ------------------------------------------------------ 5 | -- Server version 5.7.22-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `user` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `user`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `user` ( 26 | `id` int(11) NOT NULL AUTO_INCREMENT, 27 | `user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名', 28 | `iphone` char(20) NOT NULL DEFAULT '' COMMENT '手机号', 29 | `sex` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '性别 1:男 2:女 3:其它', 30 | `password` char(32) NOT NULL DEFAULT '' COMMENT '密码', 31 | `is_usable` tinyint(1) NOT NULL DEFAULT '1', 32 | PRIMARY KEY (`id`) 33 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表'; 34 | /*!40101 SET character_set_client = @saved_cs_client */; 35 | 36 | -- 37 | -- Dumping data for table `user` 38 | -- 39 | 40 | LOCK TABLES `user` WRITE; 41 | /*!40000 ALTER TABLE `user` DISABLE KEYS */; 42 | /*!40000 ALTER TABLE `user` ENABLE KEYS */; 43 | UNLOCK TABLES; 44 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 45 | 46 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 47 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 48 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 49 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 50 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 51 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 52 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 53 | 54 | -- Dump completed on 2019-01-18 16:08:18 55 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eux 4 | 5 | source scripts/version.sh 6 | source scripts/.variables.sh 7 | 8 | ProjectName=${ProjectName:-"github.com/xiaomeng79/istio-micro"} 9 | Version=${Version:-"unknown-version"} 10 | GoVersion=${GoVersion:-$(go version)} 11 | #GoVersion=${GoVersion:-$(go version | awk '{print $3}')} 12 | GitCommit=${GitCommit:-$(git rev-parse --short HEAD 2> /dev/null || true)} 13 | BuiltTime=${BuiltTime:-$(date --utc --rfc-3339 ns 2> /dev/null | sed -e 's/ /T/')} 14 | #TARGET=${TARGET:-'main'} 15 | #GOOS=$(go env GOHOSTOS) 16 | # 17 | #if [ "${GOOS}" = "windows" ]; then 18 | # TARGET="${TARGET}.exe" 19 | #fi 20 | 21 | 22 | #build 23 | build() { 24 | #判断bin是否存在 25 | if [ ! -d deployments/bin ];then 26 | mkdir -p deployments/bin 27 | fi 28 | #build 29 | 30 | dirname=./cmd/$1 31 | if [ -d $dirname ];then 32 | for f in $dirname/$2.go; do \ 33 | if [ -f $f ];then \ 34 | 35 | # CGO_ENABLED=0 GOOS=linux go build -mod=vendor -a -installsuffix cgo -ldflags '-w' -i -o deployments/bin/$1_$2/$1_$2 -tags $1_$2 ./cmd/$1/ 36 | buildapp $1 $2 37 | echo build over: $1_$2; \ 38 | fi \ 39 | done \ 40 | fi 41 | } 42 | 43 | #build app 44 | buildapp() { 45 | mkdir -p deployments/bin/${1}_${2}/sqlupdate && \ 46 | CGO_ENABLED=0 GOOS=linux go build -mod=vendor -a -installsuffix cgo -ldflags \ 47 | " \ 48 | -w \ 49 | -X '${ProjectName}/version.Version=${Version}' \ 50 | -X '${ProjectName}/version.GoVersion=${GoVersion}' \ 51 | -X '${ProjectName}/version.GitCommit=${GitCommit}' \ 52 | -X '${ProjectName}/version.BuiltTime=${BuiltTime}' \ 53 | " \ 54 | -i -o deployments/bin/${1}_${2}/${1}_${2} -tags ${1}_${2} ./cmd/${1}/ 55 | } 56 | 57 | #全部build 58 | allbuild() { 59 | build srv user 60 | build srv account 61 | build srv socket 62 | build api backend 63 | build api frontend 64 | } 65 | #判断如何build 66 | case $1 in 67 | allbuild) echo "全部build" 68 | allbuild 69 | ;; 70 | build) echo "build:"$2,$3 71 | if [ -z $2 -o -z $3 ];then 72 | echo "参数错误" 73 | exit 2 74 | fi 75 | build $2 $3 76 | ;; 77 | *) 78 | echo "build error" 79 | exit 2 80 | ;; 81 | esac 82 | -------------------------------------------------------------------------------- /internal/gateway/handlers.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "path" 7 | "strings" 8 | 9 | "github.com/xiaomeng79/go-log" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/connectivity" 12 | ) 13 | 14 | // swaggerServer returns swagger specification files located under "/swagger/" 15 | func swaggerServer(dir string) http.HandlerFunc { 16 | return func(w http.ResponseWriter, r *http.Request) { 17 | if !strings.HasSuffix(r.URL.Path, ".swagger.json") { 18 | log.Errorf("Not Found: %s", r.URL.Path) 19 | http.NotFound(w, r) 20 | return 21 | } 22 | 23 | log.Infof("Serving %s", r.URL.Path) 24 | p := strings.TrimPrefix(r.URL.Path, "/swagger/") 25 | p = path.Join(dir, p) 26 | http.ServeFile(w, r, p) 27 | } 28 | } 29 | 30 | // allowCORS allows Cross Origin Resoruce Sharing from any origin.// Don't do this without consideration in production systems. 31 | func allowCORS(h http.Handler) http.Handler { 32 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 33 | if origin := r.Header.Get("Origin"); origin != "" { 34 | w.Header().Set("Access-Control-Allow-Origin", origin) 35 | if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" { 36 | preflightHandler(w, r) 37 | return 38 | } 39 | } 40 | h.ServeHTTP(w, r) 41 | }) 42 | } 43 | 44 | // preflightHandler adds the necessary headers in order to serve// CORS from any origin using the methods "GET", "HEAD", "POST", "PUT", "DELETE"// We insist, don't do this without consideration in production systems. 45 | func preflightHandler(w http.ResponseWriter, r *http.Request) { 46 | headers := []string{"Content-Type", "Accept"} 47 | w.Header().Set("Access-Control-Allow-Headers", strings.Join(headers, ",")) 48 | methods := []string{"GET", "HEAD", "POST", "PUT", "DELETE"} 49 | w.Header().Set("Access-Control-Allow-Methods", strings.Join(methods, ",")) 50 | log.Infof("preflight request for %s", r.URL.Path) 51 | } 52 | 53 | // healthzServer returns a simple health handler which returns ok. 54 | func healthzServer(conn *grpc.ClientConn) http.HandlerFunc { 55 | return func(w http.ResponseWriter, r *http.Request) { 56 | w.Header().Set("Content-Type", "text/plain") 57 | if s := conn.GetState(); s != connectivity.Ready { 58 | http.Error(w, fmt.Sprintf("grpc server is %s", s), http.StatusBadGateway) 59 | return 60 | } 61 | fmt.Fprintln(w, "ok") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /api/frontend/run.go: -------------------------------------------------------------------------------- 1 | package frontend 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/xiaomeng79/istio-micro/cinit" 8 | "github.com/xiaomeng79/istio-micro/internal/api" 9 | "github.com/xiaomeng79/istio-micro/internal/wrapper" 10 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 11 | 12 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 13 | grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" 14 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 15 | "github.com/labstack/echo" 16 | "github.com/labstack/echo/middleware" 17 | "github.com/xiaomeng79/go-log" 18 | "google.golang.org/grpc" 19 | "google.golang.org/grpc/codes" 20 | ) 21 | 22 | // 定义services名称 23 | const ( 24 | SN = "api-frontend" 25 | ) 26 | 27 | var ( 28 | UserClient pb.UserServiceClient 29 | ) 30 | 31 | // 运行 32 | func Run() { 33 | // 初始化 34 | cinit.InitOption(SN, cinit.Trace) 35 | // 建立客户端连接 36 | grOpts := []grpc_retry.CallOption{ 37 | grpc_retry.WithCodes(codes.Aborted, codes.DeadlineExceeded), 38 | grpc_retry.WithMax(3), 39 | grpc_retry.WithPerRetryTimeout(15 * time.Second), 40 | } 41 | conn, err := grpc.Dial(cinit.Config.SrvUser.Address, 42 | grpc.WithInsecure(), 43 | grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( 44 | grpc_opentracing.UnaryClientInterceptor(), 45 | wrapper.LoggingUnaryClientInterceptor(), 46 | grpc_retry.UnaryClientInterceptor(grOpts...), 47 | // wrapper.TraceingUnaryClientInterceptor(), 48 | ))) 49 | if err != nil { 50 | log.Fatal("连接user服务失败" + err.Error()) 51 | } 52 | // 注册客户端 53 | UserClient = pb.NewUserServiceClient(conn) 54 | 55 | // 注册路由 56 | e := echo.New() 57 | e.Use(middleware.Recover()) 58 | e.Use(middleware.Logger()) 59 | e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ 60 | AllowOrigins: []string{"*"}, 61 | AllowHeaders: []string{"*"}, 62 | AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE, echo.PATCH}, 63 | })) 64 | // e.Use(common.Opentracing) 65 | e.Use(api.TraceHeader) 66 | // e.Use(api.NoSign) 67 | // 总分组 68 | g := e.Group("/frontend/v1") 69 | // g := e.Group("/backend/v1", api.JWT) 70 | // 用户 71 | g.GET("/user", UserQueryAll) 72 | 73 | // check 74 | check := e.Group("/frontend/check") 75 | check.GET("/health", func(c echo.Context) error { 76 | return c.String(http.StatusOK, "ok") 77 | }) 78 | 79 | // 启动service 80 | if err := e.Start(cinit.Config.APIFrontend.Port); err != nil { 81 | log.Fatal("启动服务失败" + err.Error()) 82 | } 83 | defer conn.Close() 84 | } 85 | -------------------------------------------------------------------------------- /srv/socket/run.go: -------------------------------------------------------------------------------- 1 | package socket 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/xiaomeng79/istio-micro/cinit" 8 | _ "github.com/xiaomeng79/istio-micro/srv/socket/statik" // 打包静态文件 9 | 10 | "github.com/Shopify/sarama" 11 | socketio "github.com/googollee/go-socket.io" 12 | "github.com/rakyll/statik/fs" 13 | "github.com/xiaomeng79/go-log" 14 | ) 15 | 16 | const ( 17 | SN = "srv-socket" // 定义services名称 18 | ) 19 | 20 | func Run() { 21 | ctx, cancel := context.WithCancel(context.Background()) 22 | // 初始化 23 | cinit.InitOption(SN, cinit.Trace, cinit.Kafka) 24 | server, err := socketio.NewServer(nil) 25 | if err != nil { 26 | log.Fatal(err.Error()) 27 | } 28 | go broadcast(ctx, server) 29 | _ = server.On("connection", func(so socketio.Socket) { 30 | _ = so.On("init", func(msg string) { 31 | _ = so.Join("match") 32 | _ = so.Emit("init", msg) 33 | }) 34 | _ = so.On("send", func(msg string) { 35 | _ = so.Emit("recive", "your msg is:"+msg) 36 | }) 37 | _ = so.On("ack", func(msg string) string { 38 | return "ack msg:" + msg 39 | }) 40 | _ = so.On("disconnection", func() { 41 | }) 42 | }) 43 | _ = server.On("error", func(so socketio.Socket, err error) { 44 | log.Error(err.Error()) 45 | }) 46 | 47 | statikFS, err := fs.New() 48 | if err != nil { 49 | log.Fatal(err.Error()) 50 | } 51 | 52 | http.Handle("/socket.io/", server) 53 | http.Handle("/public/", http.StripPrefix("/public/", http.FileServer(statikFS))) 54 | if err := http.ListenAndServe(cinit.Config.SrvSocket.Port, nil); err != nil { 55 | log.Fatal("failed to listen: " + err.Error()) 56 | } 57 | defer cancel() 58 | defer func() { 59 | if r := recover(); r != nil { 60 | log.Errorf("socket main:%+v", r) 61 | } 62 | }() 63 | } 64 | 65 | func broadcast(ctx context.Context, server *socketio.Server) { 66 | defer func() { 67 | if r := recover(); r != nil { 68 | log.Errorf("socket broadcast:%+v", r) 69 | } 70 | }() 71 | // 接受通知 72 | // topics := []string{cinit.TOPIC_SRV_KEY_CHANGE} 73 | // cinit.Kf.ConsumerGroup(ctx, GID, topics, func(message *sarama.ConsumerMessage) { 74 | // log.Debugf("msg:%+s", string(message.Value)) 75 | // sign := new(utils.SocketSign) 76 | // sign.K = string(message.Value) 77 | // server.BroadcastTo("match", sign.K, sign.String()) 78 | // }) 79 | cinit.Kf.Consumer(ctx, cinit.TopicSrvKeyChange, 0, -1, func(message *sarama.ConsumerMessage) { 80 | log.Debugf("msg:%+s", string(message.Value)) 81 | server.BroadcastTo("match", "broadcast", string(message.Value)) 82 | }, func(consumerError *sarama.ConsumerError) { 83 | log.Errorf("topic:%s,part:%d,error:%+v", consumerError.Topic, consumerError.Partition, consumerError.Err) 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /internal/metrics/prometheus/metrics.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "time" 7 | 8 | "github.com/labstack/echo" 9 | "github.com/prometheus/client_golang/prometheus" 10 | "github.com/prometheus/client_golang/prometheus/promhttp" 11 | "github.com/xiaomeng79/go-log" 12 | ) 13 | 14 | var defaultLabelNames = []string{"node", "host", "status"} 15 | 16 | func MetricsFunc(options ...Option) echo.MiddlewareFunc { 17 | opts := applyOptions(options...) 18 | 19 | reqCount := prometheus.NewCounterVec( 20 | prometheus.CounterOpts{ 21 | Namespace: opts.Namespace, 22 | Subsystem: opts.Subsystem, 23 | Name: "request_total", 24 | Help: "Total request count.", 25 | }, 26 | defaultLabelNames, 27 | ) 28 | 29 | reqDur := prometheus.NewSummaryVec( 30 | prometheus.SummaryOpts{ 31 | Namespace: opts.Namespace, 32 | Subsystem: opts.Subsystem, 33 | Name: "request_duration", 34 | Help: "Request duration in nanoseconds.", 35 | }, 36 | defaultLabelNames, 37 | ) 38 | 39 | reqSize := prometheus.NewSummaryVec( 40 | prometheus.SummaryOpts{ 41 | Namespace: opts.Namespace, 42 | Subsystem: opts.Subsystem, 43 | Name: "request_size", 44 | Help: "Request size in bytes.", 45 | }, 46 | defaultLabelNames, 47 | ) 48 | 49 | resSize := prometheus.NewSummaryVec( 50 | prometheus.SummaryOpts{ 51 | Namespace: opts.Namespace, 52 | Subsystem: opts.Subsystem, 53 | Name: "response_size", 54 | Help: "Response size in bytes.", 55 | }, 56 | defaultLabelNames, 57 | ) 58 | 59 | opts.Registry.MustRegister(reqCount, reqDur, reqSize, resSize) 60 | 61 | hostname, err := os.Hostname() 62 | if err != nil { 63 | log.Error("os.Hostname() error:" + err.Error()) 64 | hostname = "-" 65 | } 66 | 67 | return func(next echo.HandlerFunc) echo.HandlerFunc { 68 | return func(c echo.Context) error { 69 | req := c.Request() 70 | res := c.Response() 71 | 72 | // 拦截metrics path,默认"/metrics" 73 | if req.URL.Path == opts.MetricsPath { 74 | promhttp.Handler().ServeHTTP(c.Response(), c.Request()) 75 | return nil 76 | } 77 | 78 | start := time.Now() 79 | if err := next(c); err != nil { 80 | c.Error(err) 81 | } 82 | 83 | latency := time.Since(start) 84 | status := strconv.Itoa(res.Status) 85 | 86 | reqCount.WithLabelValues(hostname, req.Host, status).Inc() 87 | reqDur.WithLabelValues(hostname, req.Host, status).Observe(float64(latency.Nanoseconds())) 88 | reqSize.WithLabelValues(hostname, req.Host, status).Observe(float64(req.ContentLength)) 89 | resSize.WithLabelValues(hostname, req.Host, status).Observe(float64(res.Size)) 90 | 91 | return nil 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /srv/account/proto/google/api/httpbody.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// 2 | 3 | syntax = "proto3"; 4 | 5 | package google.api; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | option cc_enable_arenas = true; 10 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; 11 | option java_multiple_files = true; 12 | option java_outer_classname = "HttpBodyProto"; 13 | option java_package = "com.google.api"; 14 | option objc_class_prefix = "GAPI"; 15 | // Message that represents an arbitrary HTTP body. It should only be used for// payload formats that can't be represented as JSON, such as raw binary or// an HTML page.// // // This message can be used both in streaming and non-streaming API methods in// the request as well as the response.// // It can be used as a top-level request field, which is convenient if one// wants to extract parameters from either the URL or HTTP template into the// request fields and also want access to the raw HTTP body.// // Example:// // message GetResourceRequest {// // A unique request id.// string request_id = 1;// // // The raw HTTP body is bound to this field.// google.api.HttpBody http_body = 2;// }// // service ResourceService {// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);// rpc UpdateResource(google.api.HttpBody) returns// (google.protobuf.Empty);// }// // Example with streaming methods:// // service CaldavService {// rpc GetCalendar(stream google.api.HttpBody)// returns (stream google.api.HttpBody);// rpc UpdateCalendar(stream google.api.HttpBody)// returns (stream google.api.HttpBody);// }// // Use of this type only changes how the request and response bodies are// handled, all other features will continue to work unchanged. 16 | message HttpBody { 17 | // The HTTP Content-Type header value specifying the content type of the body. 18 | string content_type = 1; 19 | 20 | // The HTTP request/response body as raw binary. 21 | bytes data = 2; 22 | 23 | // Application specific response metadata. Must be set in the first response 24 | // for streaming APIs. 25 | repeated google.protobuf.Any extensions = 3; 26 | } -------------------------------------------------------------------------------- /srv/user/proto/google/api/httpbody.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// 2 | 3 | syntax = "proto3"; 4 | 5 | package google.api; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | option cc_enable_arenas = true; 10 | option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody"; 11 | option java_multiple_files = true; 12 | option java_outer_classname = "HttpBodyProto"; 13 | option java_package = "com.google.api"; 14 | option objc_class_prefix = "GAPI"; 15 | // Message that represents an arbitrary HTTP body. It should only be used for// payload formats that can't be represented as JSON, such as raw binary or// an HTML page.// // // This message can be used both in streaming and non-streaming API methods in// the request as well as the response.// // It can be used as a top-level request field, which is convenient if one// wants to extract parameters from either the URL or HTTP template into the// request fields and also want access to the raw HTTP body.// // Example:// // message GetResourceRequest {// // A unique request id.// string request_id = 1;// // // The raw HTTP body is bound to this field.// google.api.HttpBody http_body = 2;// }// // service ResourceService {// rpc GetResource(GetResourceRequest) returns (google.api.HttpBody);// rpc UpdateResource(google.api.HttpBody) returns// (google.protobuf.Empty);// }// // Example with streaming methods:// // service CaldavService {// rpc GetCalendar(stream google.api.HttpBody)// returns (stream google.api.HttpBody);// rpc UpdateCalendar(stream google.api.HttpBody)// returns (stream google.api.HttpBody);// }// // Use of this type only changes how the request and response bodies are// handled, all other features will continue to work unchanged. 16 | message HttpBody { 17 | // The HTTP Content-Type header value specifying the content type of the body. 18 | string content_type = 1; 19 | 20 | // The HTTP request/response body as raw binary. 21 | bytes data = 2; 22 | 23 | // Application specific response metadata. Must be set in the first response 24 | // for streaming APIs. 25 | repeated google.protobuf.Any extensions = 3; 26 | } -------------------------------------------------------------------------------- /internal/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/xiaomeng79/istio-micro/cinit" 7 | 8 | jsoniter "github.com/json-iterator/go" 9 | ) 10 | 11 | var json = jsoniter.ConfigCompatibleWithStandardLibrary 12 | 13 | // Error implements the error interface. 14 | type Error struct { 15 | ID string `json:"id"` 16 | Code int32 `json:"code"` 17 | Detail string `json:"detail"` 18 | Status string `json:"status"` 19 | } 20 | 21 | func (e *Error) Error() string { 22 | b, _ := json.Marshal(e) 23 | return string(b) 24 | } 25 | 26 | // New generates a custom error. 27 | func New(detail string, code int32) error { 28 | return &Error{ 29 | ID: cinit.Config.Service.Name, 30 | Code: code, 31 | Detail: detail, 32 | Status: http.StatusText(int(code)), 33 | } 34 | } 35 | 36 | // Parse tries to parse a JSON string into an error. If that// fails, it will set the given string as the error detail. 37 | func Parse(err string) *Error { 38 | e := new(Error) 39 | errr := json.Unmarshal([]byte(err), e) 40 | if errr != nil { 41 | e.Detail = err 42 | } 43 | return e 44 | } 45 | 46 | // BadRequest generates a 400 error. 47 | func BadRequest(detail string) error { 48 | return &Error{ 49 | ID: cinit.Config.Service.Name, 50 | Code: http.StatusNotFound, 51 | Detail: detail, 52 | Status: http.StatusText(http.StatusNotFound), 53 | } 54 | } 55 | 56 | // Unauthorized generates a 401 error. 57 | func Unauthorized(detail string) error { 58 | return &Error{ 59 | ID: cinit.Config.Service.Name, 60 | Code: http.StatusUnauthorized, 61 | Detail: detail, 62 | Status: http.StatusText(http.StatusUnauthorized), 63 | } 64 | } 65 | 66 | // Forbidden generates a 403 error. 67 | func Forbidden(detail string) error { 68 | return &Error{ 69 | ID: cinit.Config.Service.Name, 70 | Code: http.StatusForbidden, 71 | Detail: detail, 72 | Status: http.StatusText(http.StatusForbidden), 73 | } 74 | } 75 | 76 | // NotFound generates a 404 error. 77 | func NotFound(detail string) error { 78 | return &Error{ 79 | ID: cinit.Config.Service.Name, 80 | Code: http.StatusNotFound, 81 | Detail: detail, 82 | Status: http.StatusText(http.StatusNotFound), 83 | } 84 | } 85 | 86 | // InternalServerError generates a 500 error. 87 | func InternalServerError(detail string) error { 88 | return &Error{ 89 | ID: cinit.Config.Service.Name, 90 | Code: http.StatusInternalServerError, 91 | Detail: detail, 92 | Status: http.StatusText(http.StatusInternalServerError), 93 | } 94 | } 95 | 96 | // Conflict generates a 409 error. 97 | func Conflict(detail string) error { 98 | return &Error{ 99 | ID: cinit.Config.Service.Name, 100 | Code: http.StatusConflict, 101 | Detail: detail, 102 | Status: http.StatusText(http.StatusConflict), 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /srv/socket/asset/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Socket.IO chat 5 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 | 23 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /internal/sqlupdate/sqlupdate.go: -------------------------------------------------------------------------------- 1 | // 这个包主要是增量更新sql 2 | 3 | package sqlupdate 4 | 5 | import ( 6 | "encoding/json" 7 | "errors" 8 | "io/ioutil" 9 | "sort" 10 | ) 11 | 12 | var ( 13 | ErrNoSQLNeedUpdate = errors.New("no sql need update") 14 | ) 15 | 16 | // sql更新 17 | type SQLUpdate struct { 18 | Project string 19 | Update []UpdateRecord 20 | } 21 | 22 | // 更新记录 23 | type UpdateRecord struct { 24 | Version string `json:"version"` 25 | Author string `json:"author"` 26 | File string `json:"file"` 27 | Date string `json:"date"` 28 | } 29 | 30 | // 解析记录 31 | func (s *SQLUpdate) decode(filename string) error { 32 | // 读取文件 33 | bs, err := ioutil.ReadFile(filename) 34 | if err != nil { 35 | return err 36 | } 37 | // 解析数据 38 | err = json.Unmarshal(bs, s) 39 | if err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | 45 | // 比较应该执行的sql 46 | func (s *SQLUpdate) compareResult(lastVersion, currentVersion string) []UpdateRecord { 47 | res := make([]UpdateRecord, 0) 48 | for _, u := range s.Update { 49 | if compare(lastVersion, u.Version) && compare(u.Version, currentVersion) { 50 | res = append(res, u) 51 | } 52 | } 53 | return res 54 | } 55 | 56 | // 比较获取需要更新的版本// 判断旧版本是否大于要比较版本,true:小于等于(需要更新) false:大于(不需要更新) 57 | func compare(ov, cv string) bool { 58 | return versionOrdinal(ov) < versionOrdinal(cv) 59 | } 60 | 61 | // 版本号顺序 62 | func versionOrdinal(version string) string { 63 | // ISO/IEC 14651:2011 64 | const maxByte = 1<<8 - 1 65 | vo := make([]byte, 0, len(version)+8) 66 | j := -1 67 | for i := 0; i < len(version); i++ { 68 | b := version[i] 69 | if '0' > b || b > '9' { 70 | vo = append(vo, b) 71 | j = -1 72 | continue 73 | } 74 | if j == -1 { 75 | vo = append(vo, 0x00) 76 | j = len(vo) - 1 77 | } 78 | if vo[j] == 1 && vo[j+1] == '0' { 79 | vo[j+1] = b 80 | continue 81 | } 82 | if vo[j]+1 > maxByte { 83 | panic("VersionOrdinal: invalid version") 84 | } 85 | vo = append(vo, b) 86 | vo[j]++ 87 | } 88 | return string(vo) 89 | } 90 | 91 | // 返回需要执行的sql 92 | func getSQL(filename string) ([]byte, error) { 93 | return ioutil.ReadFile(filename) 94 | } 95 | 96 | // 根据sql版本升级记录,新旧版本号,返回需要升级的sql 97 | func (s *SQLUpdate) GetSqls(filename, oldVersion, newVersion string) (string, error) { 98 | // 解析更新文件 99 | err := s.decode(filename) 100 | if err != nil { 101 | return "", err 102 | } 103 | // 解析需要更新的sql 104 | res := s.compareResult(oldVersion, newVersion) 105 | if len(res) == 0 { 106 | return "", ErrNoSQLNeedUpdate 107 | } 108 | // 排序sql 109 | res = updateSQLSort(res) 110 | // 获取需要更新的sql 111 | result := make([]byte, 0) 112 | for _, v := range res { 113 | bs, err := getSQL(v.File) 114 | if err != nil { 115 | return "", err 116 | } 117 | result = append(result, bs...) 118 | } 119 | 120 | return string(result), nil 121 | } 122 | 123 | // 排序 124 | func updateSQLSort(ur []UpdateRecord) []UpdateRecord { 125 | sort.Slice(ur, func(i, j int) bool { 126 | return ur[i].Version < ur[j].Version 127 | }) 128 | return ur 129 | } 130 | -------------------------------------------------------------------------------- /srv/account/model.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | 8 | "github.com/xiaomeng79/istio-micro/cinit" 9 | "github.com/xiaomeng79/istio-micro/internal/utils" 10 | pb "github.com/xiaomeng79/istio-micro/srv/account/proto" 11 | 12 | "github.com/asaskevich/govalidator" 13 | "github.com/xiaomeng79/go-log" 14 | ) 15 | 16 | type Account struct { 17 | base *pb.AccountBase 18 | Page utils.Page 19 | } 20 | 21 | // 验证参数 22 | func (m *Account) validate() error { 23 | _, err := govalidator.ValidateStruct(m.base) 24 | return err 25 | } 26 | 27 | // 验证id 28 | func (m *Account) validateID() error { 29 | if m.base.Id <= 0 { 30 | return errors.New("id必须大于0") 31 | } 32 | return nil 33 | } 34 | 35 | // 添加之前 36 | func (m *Account) beforeAdd(ctx context.Context) error { // nolint: unparam 37 | // 验证参数 38 | err := utils.V(m.validate) 39 | if err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | 45 | // 修改之前 46 | // nolint: unparam 47 | func (m *Account) beforeUpdate(ctx context.Context) error { 48 | err := utils.V(m.validateID) 49 | if err != nil { 50 | return err 51 | } 52 | return nil 53 | } 54 | 55 | // 删除之前 56 | // nolint: unparam,unused 57 | func (m *Account) beforeDelete(ctx context.Context) error { 58 | err := utils.V(m.validateID) 59 | if err != nil { 60 | return err 61 | } 62 | return nil 63 | } 64 | 65 | // 添加 ,返回id 66 | func (m *Account) Add(ctx context.Context) error { 67 | err := m.beforeAdd(ctx) 68 | if err != nil { 69 | log.Info(err.Error(), ctx) 70 | return err 71 | } 72 | _sql := `INSERT INTO account (user_id,account_level,balance,account_status) VALUES (:user_id,:account_level,:balance,:account_status) RETURNING id` 73 | stmt, err := cinit.Pg.PrepareNamed(_sql) 74 | if err != nil { 75 | log.Error(err.Error(), ctx) 76 | return err 77 | } 78 | arg := map[string]interface{}{ 79 | "user_id": m.base.UserId, 80 | "account_level": m.base.AccountLevel, 81 | "balance": m.base.Balance, 82 | "account_status": m.base.AccountStatus, 83 | } 84 | err = stmt.Get(&m.base.Id, arg) 85 | defer stmt.Close() 86 | log.Debugf("m:%+v", m.base, ctx) 87 | // m.base.ID, err = utils.ID(r, err) 88 | if err != nil { 89 | log.Error(err.Error(), ctx) 90 | return err 91 | } 92 | return nil 93 | } 94 | 95 | // 修改 96 | func (m *Account) Update(ctx context.Context) error { 97 | err := m.beforeUpdate(ctx) 98 | if err != nil { 99 | log.Info(err.Error(), ctx) 100 | return err 101 | } 102 | r, err := cinit.Pg.Exec(`UPDATE account SET balance=$1 WHERE id=$2`, 103 | m.base.Balance, m.base.Id) 104 | err = utils.R(r, err) 105 | if err != nil { 106 | log.Error(err.Error(), ctx) 107 | return err 108 | } 109 | return nil 110 | } 111 | 112 | // 查询一个 113 | func (m *Account) QueryOne(ctx context.Context) error { 114 | err := utils.V(m.validateID) 115 | if err != nil { 116 | log.Info(err.Error(), ctx) 117 | return err 118 | } 119 | 120 | err = cinit.Pg.Get(m.base, `SELECT id,user_id,account_level,balance,account_status FROM account WHERE id=$1 LIMIT 1`, m.base.Id) 121 | switch { 122 | case err == sql.ErrNoRows: 123 | log.Info(err.Error(), ctx) 124 | return nil 125 | case err != nil: 126 | log.Error(err.Error(), ctx) 127 | return err 128 | default: 129 | return nil 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | govet: 3 | check-shadowing: true 4 | settings: 5 | printf: 6 | funcs: 7 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof 8 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf 9 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf 10 | - (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf 11 | golint: 12 | min-confidence: 0 13 | gocyclo: 14 | min-complexity: 15 15 | maligned: 16 | suggest-new: true 17 | dupl: 18 | threshold: 100 19 | goconst: 20 | min-len: 2 21 | min-occurrences: 2 22 | depguard: 23 | list-type: blacklist 24 | packages: 25 | - github.com/sirupsen/logrus 26 | packages-with-error-messages: 27 | github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" 28 | misspell: 29 | locale: US 30 | ignore-words: 31 | - someword 32 | lll: 33 | line-length: 260 34 | goimports: 35 | local-prefixes: github.com/xiaomeng79/istio-micro 36 | gocritic: 37 | enabled-tags: 38 | - diagnostic 39 | - experimental 40 | - opinionated 41 | - performance 42 | - style 43 | disabled-checks: 44 | - wrapperFunc 45 | - commentedOutCode 46 | - dupImport # https://github.com/go-critic/go-critic/issues/845 47 | funlen: 48 | lines: 100 49 | statements: 50 50 | gosec: 51 | 52 | linters: 53 | # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint 54 | disable-all: true 55 | enable: 56 | - bodyclose 57 | - deadcode 58 | - depguard 59 | - dogsled 60 | # - dupl 61 | - errcheck 62 | - funlen 63 | - gochecknoinits 64 | - goconst 65 | - gocritic 66 | - gocyclo 67 | - gofmt 68 | - goimports 69 | - golint 70 | - gosec 71 | - gosimple 72 | - govet 73 | - ineffassign 74 | - interfacer 75 | - lll 76 | - misspell 77 | - nakedret 78 | - scopelint 79 | - staticcheck 80 | - structcheck 81 | - stylecheck 82 | - typecheck 83 | - unconvert 84 | - errcheck 85 | - unparam 86 | - unused 87 | - varcheck 88 | - whitespace 89 | 90 | # don't enable: 91 | # - gochecknoglobals 92 | # - gocognit 93 | # - godox 94 | # - maligned 95 | # - prealloc 96 | 97 | issues: 98 | exclude: 99 | - G108 100 | # # Exclude some linters from running on tests files. 101 | # - path: srv/account/proto 102 | ## - linters: 103 | ## - lll 104 | ## source: "^//go:generate " 105 | # exclude-use-default: false 106 | max-same-issues: 5 107 | 108 | run: 109 | # default concurrency is a available CPU number 110 | concurrency: 4 111 | 112 | # timeout for analysis, e.g. 30s, 5m, default is 1m 113 | timeout: 1m 114 | 115 | # exit code when at least one issue was found, default is 1 116 | issues-exit-code: 1 117 | 118 | # include test files or not, default is true 119 | tests: true 120 | 121 | skip-dirs: 122 | - docs/ 123 | skip-files: 124 | - ".*\\.my\\.go$" 125 | 126 | # golangci.com configuration 127 | # https://github.com/golangci/golangci/wiki/Configuration 128 | service: 129 | golangci-lint-version: 1.19.x # use the fixed version to not introduce new linters unexpectedly 130 | prepare: 131 | - echo "here I can run custom commands, but no preparation needed for this repo" -------------------------------------------------------------------------------- /deployments/config/postgres/init.sql.bak: -------------------------------------------------------------------------------- 1 | -- 2 | -- PostgreSQL database dump 3 | -- 4 | 5 | -- Dumped from database version 11.4 6 | -- Dumped by pg_dump version 11.4 7 | create database test with template template0 lc_collate "zh_CN.UTF8" lc_ctype "zh_CN.UTF8" encoding 'UTF8'; 8 | \c test 9 | 10 | SET statement_timeout = 0; 11 | SET lock_timeout = 0; 12 | SET idle_in_transaction_session_timeout = 0; 13 | SET client_encoding = 'UTF8'; 14 | SET standard_conforming_strings = on; 15 | SELECT pg_catalog.set_config('search_path', '', false); 16 | SET check_function_bodies = false; 17 | SET xmloption = content; 18 | SET client_min_messages = warning; 19 | SET row_security = off; 20 | 21 | SET default_tablespace = ''; 22 | 23 | SET default_with_oids = false; 24 | 25 | -- 26 | -- Name: account; Type: TABLE; Schema: public; Owner: postgres 27 | -- 28 | 29 | CREATE TABLE public.account ( 30 | id integer NOT NULL, 31 | user_id integer DEFAULT 0 NOT NULL, 32 | account_level smallint DEFAULT 0 NOT NULL, 33 | balance numeric(18,2) DEFAULT 0.00 NOT NULL, 34 | account_status smallint DEFAULT 0 NOT NULL 35 | ); 36 | 37 | 38 | ALTER TABLE public.account OWNER TO postgres; 39 | 40 | -- 41 | -- Name: TABLE account; Type: COMMENT; Schema: public; Owner: postgres 42 | -- 43 | 44 | COMMENT ON TABLE public.account IS '账户表'; 45 | 46 | 47 | -- 48 | -- Name: COLUMN account.user_id; Type: COMMENT; Schema: public; Owner: postgres 49 | -- 50 | 51 | COMMENT ON COLUMN public.account.user_id IS '用户id'; 52 | 53 | 54 | -- 55 | -- Name: COLUMN account.account_level; Type: COMMENT; Schema: public; Owner: postgres 56 | -- 57 | 58 | COMMENT ON COLUMN public.account.account_level IS '账户级别'; 59 | 60 | 61 | -- 62 | -- Name: COLUMN account.account; Type: COMMENT; Schema: public; Owner: postgres 63 | -- 64 | 65 | COMMENT ON COLUMN public.account.balance IS '账户余额'; 66 | 67 | 68 | -- 69 | -- Name: COLUMN account.account_status; Type: COMMENT; Schema: public; Owner: postgres 70 | -- 71 | 72 | COMMENT ON COLUMN public.account.account_status IS '账户状态'; 73 | 74 | 75 | -- 76 | -- Name: account_idSeq; Type: SEQUENCE; Schema: public; Owner: postgres 77 | -- 78 | 79 | CREATE SEQUENCE public.account_idSeq 80 | AS integer 81 | START WITH 1 82 | INCREMENT BY 1 83 | NO MINVALUE 84 | NO MAXVALUE 85 | CACHE 1; 86 | 87 | 88 | ALTER TABLE public.account_idSeq OWNER TO postgres; 89 | 90 | -- 91 | -- Name: account_idSeq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres 92 | -- 93 | 94 | ALTER SEQUENCE public.account_idSeq OWNED BY public.account.id; 95 | 96 | 97 | -- 98 | -- Name: account id; Type: DEFAULT; Schema: public; Owner: postgres 99 | -- 100 | 101 | ALTER TABLE ONLY public.account ALTER COLUMN id SET DEFAULT nextval('public.account_idSeq'::regclass); 102 | 103 | 104 | -- 105 | -- Data for Name: account; Type: TABLE DATA; Schema: public; Owner: postgres 106 | -- 107 | 108 | COPY public.account (id, user_id, account_level, account, account_status) FROM stdin; 109 | \. 110 | 111 | 112 | -- 113 | -- Name: account_idSeq; Type: SEQUENCE SET; Schema: public; Owner: postgres 114 | -- 115 | 116 | SELECT pg_catalog.setval('public.account_idSeq', 1, false); 117 | 118 | 119 | -- 120 | -- Name: account account_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres 121 | -- 122 | 123 | ALTER TABLE ONLY public.account 124 | ADD CONSTRAINT account_pkey PRIMARY KEY (id); 125 | 126 | 127 | -- 128 | -- PostgreSQL database dump complete 129 | -- 130 | 131 | create external pg_trgm; -------------------------------------------------------------------------------- /deployments/docker-compose-bak.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | #公共的组件 4 | redis: 5 | image: redis:5.0.3-alpine3.8 6 | restart: always 7 | volumes: 8 | - redis-data-user:/var/lib/redis 9 | ports: 10 | - "6379:6379" 11 | mysqldb: 12 | image: 'mysql/mysql-server:5.7' 13 | restart: always 14 | command: --default-authentication-plugin=mysql_native_password 15 | environment: 16 | MYSQL_ROOT_PASSWORD: root 17 | MYSQL_DATABASE: user 18 | MYSQL_USER: test 19 | MYSQL_PASSWORD: test 20 | volumes: 21 | - mysql-data-user:/var/lib/mysql 22 | - ./config/mysql/my.cnf:/etc/my.cnf 23 | - ./config/mysql/:/docker-entrypoint-initdb.d/ 24 | ports: 25 | - '3306:3306' 26 | zookeeper: 27 | image: wurstmeister/zookeeper 28 | restart: always 29 | ports: 30 | - "2181:2181" 31 | kafka: 32 | image: wurstmeister/kafka 33 | restart: always 34 | ports: 35 | - "9092:9092" 36 | depends_on: 37 | - zookeeper 38 | environment: 39 | KAFKA_ADVERTISED_HOST_NAME: 192.168.2.103 40 | KAFKA_CREATE_TOPICS: "test:1:2" 41 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 42 | volumes: 43 | - /var/run/docker.sock:/var/run/docker.sock 44 | jaeger: 45 | image: jaegertracing/all-in-one:1.7 46 | restart: always 47 | ports: 48 | - "5775:5775/udp" 49 | - "6831:6831/udp" 50 | - "6832:6832/udp" 51 | - "5778:5778" 52 | - "16686:16686" 53 | - "14268:14268" 54 | - "9411:9411" 55 | # consul: 56 | # command: -server -bootstrap -rejoin 57 | # image: consul:latest 58 | # ports: 59 | # - "8300:8300" 60 | # - "8400:8400" 61 | # - "8500:8500" 62 | # - "8600:53/udp" 63 | influxdb: 64 | image: influxdb:1.5-alpine 65 | ports: 66 | - "8086:8086" 67 | grafana: 68 | image: grafana/grafana:latest 69 | ports: 70 | - "3000:3000" 71 | 72 | #项目 73 | srv_user: 74 | build: ./bin/srv_user 75 | restart: always 76 | depends_on: 77 | - redis 78 | - mysqldb 79 | - kafka 80 | - jaeger 81 | environment: 82 | CONFIGOR_TRACE_ADDRESS: jaeger:6831 83 | CONFIGOR_MYSQL_ADDR: mysqldb 84 | CONFIGOR_MYSQL_PORT: 3306 85 | CONFIGOR_MYSQL_DBNAME: user 86 | CONFIGOR_MYSQL_USER: test 87 | CONFIGOR_MYSQL_PASSWORD: test 88 | CONFIGOR_REDIS_ADDR: redis:6379 89 | CONFIGOR_REDIS_PASSWORD: 90 | CONFIGOR_KAFKA_ADDRS: kafka:9092 91 | ports: 92 | - "5001:5001" 93 | srv_socket: 94 | build: ./bin/srv_socket 95 | restart: always 96 | depends_on: 97 | - kafka 98 | - jaeger 99 | environment: 100 | CONFIGOR_TRACE_ADDRESS: jaeger:6831 101 | CONFIGOR_KAFKA_ADDRS: kafka:9092 102 | ports: 103 | - "5002:5002" 104 | api_backend: 105 | build: ./bin/api_backend 106 | restart: always 107 | depends_on: 108 | - jaeger 109 | - srv_user 110 | - influxdb 111 | - grafana 112 | environment: 113 | CONFIGOR_TRACE_ADDRESS: jaeger:6831 114 | CONFIGOR_SRVUSER_ADDRESS: srv_user:5001 115 | ports: 116 | - "8888:8888" 117 | api_frontend: 118 | build: ./bin/api_frontend 119 | restart: always 120 | depends_on: 121 | - jaeger 122 | - srv_user 123 | environment: 124 | CONFIGOR_TRACE_ADDRESS: jaeger:6831 125 | CONFIGOR_SRVUSER_ADDRESS: srv_user:5001 126 | ports: 127 | - "8889:8889" 128 | 129 | volumes: 130 | mysql-data-user: 131 | redis-data-user: -------------------------------------------------------------------------------- /api/backend/run.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/xiaomeng79/istio-micro/cinit" 8 | "github.com/xiaomeng79/istio-micro/internal/api" 9 | "github.com/xiaomeng79/istio-micro/internal/metrics" 10 | "github.com/xiaomeng79/istio-micro/internal/wrapper" 11 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 12 | 13 | grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 14 | grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry" 15 | grpc_opentracing "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" 16 | "github.com/labstack/echo" 17 | "github.com/labstack/echo/middleware" 18 | "github.com/xiaomeng79/go-log" 19 | "google.golang.org/grpc" 20 | "google.golang.org/grpc/codes" 21 | ) 22 | 23 | // 定义services名称 24 | const ( 25 | SN = "api-backend" 26 | ) 27 | 28 | var ( 29 | UserClient pb.UserServiceClient 30 | ) 31 | 32 | // 运行 33 | func Run() { 34 | // 初始化 35 | cinit.InitOption(SN, cinit.Trace) 36 | // 建立客户端连接 37 | grOpts := []grpc_retry.CallOption{ 38 | grpc_retry.WithCodes(codes.Aborted, codes.DeadlineExceeded), 39 | grpc_retry.WithMax(3), 40 | grpc_retry.WithPerRetryTimeout(15 * time.Second), 41 | } 42 | conn, err := grpc.Dial(cinit.Config.SrvUser.Address, 43 | grpc.WithInsecure(), 44 | grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient( 45 | grpc_opentracing.UnaryClientInterceptor(), 46 | wrapper.LoggingUnaryClientInterceptor(), 47 | grpc_retry.UnaryClientInterceptor(grOpts...), 48 | // wrapper.TraceingUnaryClientInterceptor(), 49 | ))) 50 | if err != nil { 51 | log.Fatal("连接user服务失败" + err.Error()) 52 | } 53 | // 注册客户端 54 | UserClient = pb.NewUserServiceClient(conn) 55 | 56 | // 注册路由 57 | e := echo.New() 58 | e.Use(middleware.Recover()) 59 | e.Use(middleware.Logger()) 60 | e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ 61 | AllowOrigins: []string{"*"}, 62 | AllowHeaders: []string{"*"}, 63 | AllowMethods: []string{echo.GET, echo.PUT, echo.POST, echo.DELETE, echo.PATCH}, 64 | })) 65 | // e.Use(common.Opentracing) 66 | e.Use(api.TraceHeader) 67 | // e.Use(api.NoSign) 68 | 69 | // metrics 70 | // Metrics 71 | if cinit.Config.Metrics.Enable == "yes" { 72 | // Push模式 73 | m := metrics.NewMetrics() 74 | e.Use(api.MetricsFunc(m)) 75 | m.MemStats() 76 | // InfluxDB 77 | m.InfluxDBWithTags( 78 | time.Duration(cinit.Config.Metrics.Duration)*time.Second, 79 | cinit.Config.Metrics.URL, 80 | cinit.Config.Metrics.Database, 81 | cinit.Config.Metrics.UserName, 82 | cinit.Config.Metrics.Password, 83 | map[string]string{"service": SN}, 84 | ) 85 | 86 | // Graphite 87 | // addr, _ := net.ResolveTCPAddr("tcp", Conf.Metrics.Address) 88 | // m.Graphite(Conf.Metrics.FreqSec*time.Second, "echo-web.node."+hostname, addr) 89 | } 90 | 91 | // 总分组 92 | g := e.Group("/backend/v1") 93 | // g := e.Group("/backend/v1", api.JWT) 94 | // 用户 95 | g.POST("/user", UserAdd) 96 | g.PUT("/user/:id", UserUpdate) 97 | g.DELETE("/user/:id", UserDelete) 98 | g.GET("/user/:id", UserQueryOne) 99 | g.GET("/user", UserQueryAll) 100 | 101 | // 总分组 102 | gu := e.Group("/backend/v1") 103 | gu.POST("/login", Login) 104 | // check 105 | check := e.Group("/backend/check") 106 | check.GET("/health", func(c echo.Context) error { 107 | return c.String(http.StatusOK, "ok") 108 | }) 109 | 110 | // 启动service 111 | if err := e.Start(cinit.Config.APIBackend.Port); err != nil { 112 | log.Fatal("启动服务失败" + err.Error()) 113 | } 114 | defer conn.Close() 115 | } 116 | -------------------------------------------------------------------------------- /internal/api/middle.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | "time" 7 | 8 | "github.com/xiaomeng79/istio-micro/cinit" 9 | "github.com/xiaomeng79/istio-micro/internal/jwt" 10 | metrics2 "github.com/xiaomeng79/istio-micro/internal/metrics" 11 | 12 | "github.com/labstack/echo" 13 | "github.com/rcrowley/go-metrics" 14 | "github.com/xiaomeng79/go-log" 15 | ) 16 | 17 | func VerifyParam(next echo.HandlerFunc) echo.HandlerFunc { 18 | return func(c echo.Context) error { 19 | // 获取span 20 | ctx := c.Request().Context() 21 | log.Infof("req:%+v", c.Request().Body, ctx) 22 | // 解析公共参数 23 | r := new(ReqParam) 24 | err := c.Bind(r) 25 | if err != nil { 26 | log.Info("解析参数错误:"+err.Error(), ctx) 27 | return HandleError(c, CommonParamConvertError) 28 | } 29 | log.Infof("decode req:%+v", r, ctx) 30 | // 验证公共参数 31 | err = r.Validate() 32 | if err != nil { 33 | log.Info("验证参数错误:"+err.Error(), ctx) 34 | return HandleError(c, CommonParamConvertError, err.Error()) 35 | } 36 | // 请求appsecret 37 | 38 | // 验证签名 39 | b, err := r.CompareSign() 40 | if !b { 41 | log.Info("获取appsecret"+err.Error(), ctx) 42 | return HandleError(c, CommonSignError) 43 | } 44 | // 通过验证,绑定参数 45 | c.Set(cinit.ReqParam, r) 46 | return next(c) 47 | } 48 | } 49 | 50 | // 验证jwt 51 | func JWT(next echo.HandlerFunc) echo.HandlerFunc { 52 | return func(c echo.Context) error { 53 | // 获取span 54 | ctx := c.Request().Context() 55 | // 从请求头获取token信息 56 | jwtString := c.Request().Header.Get(cinit.JWTName) 57 | // log.Debug(jwtString, ctx) 58 | // 解析JWT 59 | auths := strings.Split(jwtString, " ") 60 | if !strings.EqualFold(auths[0], "BEARER") || auths[1] == " " { 61 | return HandleError(c, ReqTokenError, "token验证失败") 62 | } 63 | jwtmsg, err := jwt.Decode(strings.Trim(auths[1], " ")) 64 | if err != nil { 65 | log.Info(err.Error(), ctx) 66 | return HandleError(c, ReqTokenError, "token验证失败") 67 | } 68 | c.Set(cinit.JWTMsg, jwtmsg) 69 | return next(c) 70 | } 71 | } 72 | 73 | func TraceHeader(next echo.HandlerFunc) echo.HandlerFunc { 74 | return func(c echo.Context) error { 75 | log.Infof("header:%+v", c.Request().Header) 76 | return next(c) 77 | } 78 | } 79 | 80 | // verifyparam// trace 81 | func NoSign(next echo.HandlerFunc) echo.HandlerFunc { 82 | return func(c echo.Context) error { 83 | // 获取span 84 | // c.Set(cinit.TRACE_CONTEXT, context.Background()) 85 | return next(c) 86 | } 87 | } 88 | 89 | // metrics 90 | func MetricsFunc(m *metrics2.Metrics) echo.MiddlewareFunc { 91 | return func(next echo.HandlerFunc) echo.HandlerFunc { 92 | return func(c echo.Context) error { 93 | res := c.Response() 94 | start := time.Now() 95 | if err := next(c); err != nil { 96 | c.Error(err) 97 | } 98 | stop := time.Now() 99 | 100 | latency := stop.Sub(start) 101 | status := res.Status 102 | // Total request count. 103 | meter := metrics.GetOrRegisterMeter("status."+strconv.Itoa(status), m.GetRegistry()) 104 | meter.Mark(1) 105 | 106 | // Request size in bytes. 107 | // meter = metrics.GetOrRegisterMeter(m.WithPrefix("status."+strconv.Itoa(status)), m.GetRegistry()) 108 | // meter.Mark(req.ContentLength) 109 | 110 | // Request duration in nanoseconds. 111 | h := metrics.GetOrRegisterHistogram("h_status."+strconv.Itoa(status), m.GetRegistry(), 112 | metrics.NewExpDecaySample(1028, 0.015)) 113 | h.Update(latency.Nanoseconds()) 114 | 115 | // Response size in bytes. 116 | // meter = metrics.GetOrRegisterMeter(m.WithPrefix("status."+strconv.Itoa(status)), m.GetRegistry()) 117 | // meter.Mark(res.Size) 118 | 119 | return nil 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xiaomeng79/istio-micro 2 | 3 | require ( 4 | github.com/DataDog/zstd v1.3.4 // indirect 5 | github.com/Shopify/sarama v1.20.0 6 | github.com/Shopify/toxiproxy v2.1.3+incompatible // indirect 7 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf 8 | github.com/bsm/sarama-cluster v2.1.15+incompatible 9 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect 10 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 11 | github.com/eapache/go-resiliency v1.1.0 // indirect 12 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect 13 | github.com/eapache/queue v1.1.0 // indirect 14 | github.com/go-redis/redis v6.15.1+incompatible 15 | github.com/go-sql-driver/mysql v1.4.1 16 | github.com/gogo/protobuf v1.2.0 // indirect 17 | github.com/golang/protobuf v1.3.2 18 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect 19 | github.com/googollee/go-engine.io v0.0.0-20180829091931-e2f255711dcb // indirect 20 | github.com/googollee/go-socket.io v0.0.0-20181214084611-0ad7206c347a 21 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect 22 | github.com/gorilla/websocket v1.4.0 // indirect 23 | github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190118093823-f849b5445de4 24 | github.com/grpc-ecosystem/grpc-gateway v1.11.1 25 | github.com/influxdata/influxdb1-client v0.0.0-20190124185755-16c852ea613f // indirect 26 | github.com/jinzhu/configor v0.0.0-20180614024415-4edaf76fe188 27 | github.com/jinzhu/copier v0.0.0-20180308034124-7e38e58719c3 28 | github.com/jmoiron/sqlx v1.2.0 29 | github.com/json-iterator/go v1.1.5 30 | github.com/jtolds/gls v4.2.1+incompatible // indirect 31 | github.com/labstack/echo v3.3.5+incompatible 32 | github.com/labstack/gommon v0.2.8 // indirect 33 | github.com/lib/pq v1.2.0 34 | github.com/mattn/go-colorable v0.0.9 // indirect 35 | github.com/mattn/go-isatty v0.0.4 // indirect 36 | github.com/mitchellh/mapstructure v1.1.2 37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 38 | github.com/modern-go/reflect2 v1.0.1 // indirect 39 | github.com/onsi/ginkgo v1.7.0 // indirect 40 | github.com/onsi/gomega v1.4.3 // indirect 41 | github.com/opentracing/opentracing-go v1.0.2 42 | github.com/pierrec/lz4 v2.0.7+incompatible // indirect 43 | github.com/pkg/errors v0.8.1 // indirect 44 | github.com/prometheus/client_golang v0.9.2 45 | github.com/rakyll/statik v0.1.5 46 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a 47 | github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect 48 | github.com/sirupsen/logrus v1.3.0 // indirect 49 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect 50 | github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c // indirect 51 | github.com/spf13/cobra v0.0.4 52 | github.com/stretchr/testify v1.3.0 53 | github.com/uber/jaeger-client-go v2.15.0+incompatible 54 | github.com/uber/jaeger-lib v1.5.0+incompatible // indirect 55 | github.com/valyala/bytebufferpool v1.0.0 // indirect 56 | github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect 57 | github.com/vrischmann/go-metrics-influxdb v0.0.0-20190121110601-4457d7e0175c 58 | github.com/xiaomeng79/go-log v2.0.1+incompatible 59 | github.com/xiaomeng79/go-utils v0.0.0-20181017074258-e4925b865baa 60 | go.uber.org/atomic v1.3.2 // indirect 61 | go.uber.org/multierr v1.1.0 // indirect 62 | go.uber.org/zap v1.9.1 // indirect 63 | golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc // indirect 64 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 65 | google.golang.org/grpc v1.19.0 66 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect 67 | ) 68 | 69 | go 1.13 70 | -------------------------------------------------------------------------------- /internal/trace/trace.go.bak: -------------------------------------------------------------------------------- 1 | package trace 2 | // // // import (// "context"// "github.com/opentracing/opentracing-go"// "google.golang.org/grpc/metadata"// "net/http"// )// // //istio// // const (// prefixTracerState = "x-b3-"// zipkinTraceID = prefixTracerState + "traceid"// zipkinSpanID = prefixTracerState + "spanid"// zipkinParentSpanID = prefixTracerState + "parentspanid"// zipkinSampled = prefixTracerState + "sampled"// zipkinFlags = prefixTracerState + "flags"// )// // var otHeaders = []string{// zipkinTraceID,// zipkinSpanID,// zipkinParentSpanID,// zipkinSampled,// zipkinFlags,// }// // // // //记录tag// func tag(ctx context.Context, sp opentracing.Span) (context.Context, opentracing.Span) {// ctx = opentracing.ContextWithSpan(ctx, sp)// // // 加tag// // s := strings.Split(fmt.Sprintf("%v", sp), ":")// // if len(s) >= 3 {// // sp.SetTag("trace_id", s[0])// // sp.SetTag("span_id", s[1])// // sp.SetTag("parent_id", s[2])// // }// return ctx, sp// }// // func traceIntoContextByGlobalTracer(ctx context.Context, tracer opentracing.Tracer, name string) (context.Context, opentracing.Span, error) {// md, ok := metadata.FromIncomingContext(ctx)// if !ok {// md = make(map[string]string)// }// var sp opentracing.Span// wireContext, err := tracer.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md))// if err != nil {// sp = tracer.StartSpan(name)// } else {// sp = tracer.StartSpan(name, opentracing.ChildOf(wireContext))// }// if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil {// return nil, nil, err// }// ctx, sp = tag(ctx, sp)// ctx = metadata.NewContext(ctx, md)// return ctx, sp, nil// }// // func traceFromHeaderByGlobalTracer(ctx context.Context, tracer opentracing.Tracer, name string, header http.Header) (context.Context, opentracing.Span, error) {// var sp opentracing.Span// wireContext, err := tracer.Extract(opentracing.TextMap, opentracing.HTTPHeadersCarrier(header))// if err != nil {// sp = tracer.StartSpan(name)// } else {// sp = tracer.StartSpan(name, opentracing.ChildOf(wireContext))// }// md, ok := metadata.FromContext(ctx)// if !ok {// md = make(map[string]string)// }// if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil {// return nil, nil, err// }// ctx, sp = tag(ctx, sp)// ctx = metadata.NewContext(ctx, md)// return ctx, sp, nil// }// // func traceToHeaderByGlobalTracer(ctx context.Context, tracer opentracing.Tracer, name string, header http.Header) (context.Context, opentracing.Span, error) {// md, ok := metadata.FromContext(ctx)// if !ok {// md = make(map[string]string)// }// var sp opentracing.Span// wireContext, err := tracer.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md))// if err != nil {// sp = tracer.StartSpan(name)// } else {// sp = tracer.StartSpan(name, opentracing.ChildOf(wireContext))// }// if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.HTTPHeadersCarrier(header)); err != nil {// return nil, nil, err// }// ctx, sp = tag(ctx, sp)// return ctx, sp, nil// }// // //opentracing从context获取,写入context,适用RPC// func TraceIntoContext(ctx context.Context, name string) (context.Context, opentracing.Span, error) {// return traceIntoContextByGlobalTracer(ctx, opentracing.GlobalTracer(), name)// }// // //opentracing从header获取,写入context,适用获取http// func TraceFromHeader(ctx context.Context, name string, header http.Header) (context.Context, opentracing.Span, error) {// return traceFromHeaderByGlobalTracer(ctx, opentracing.GlobalTracer(), name, header)// }// // //opentracing从context获取,写入http,适用将调用http// func TraceToHeader(ctx context.Context, name string, header http.Header) (context.Context, opentracing.Span, error) {// return traceToHeaderByGlobalTracer(ctx, opentracing.GlobalTracer(), name, header)// } 3 | -------------------------------------------------------------------------------- /srv/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 7 | 8 | "github.com/jinzhu/copier" 9 | "github.com/xiaomeng79/go-log" 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | ) 13 | 14 | type Server struct{} 15 | 16 | func (s *Server) UserAdd(ctx context.Context, in *pb.UserBase) (out *pb.UserBase, outerr error) { 17 | m := new(User) 18 | out = new(pb.UserBase) 19 | err := copier.Copy(m, in) 20 | if err != nil { 21 | log.Error(err.Error(), ctx) 22 | outerr = status.Error(codes.Internal, err.Error()) 23 | return 24 | } 25 | err = m.Add(ctx) 26 | if err != nil { 27 | outerr = status.Error(codes.InvalidArgument, err.Error()) 28 | return 29 | } 30 | err = copier.Copy(out, m) 31 | if err != nil { 32 | log.Error(err.Error(), ctx) 33 | outerr = status.Error(codes.Internal, err.Error()) 34 | return 35 | } 36 | return 37 | } 38 | 39 | func (s *Server) UserUpdate(ctx context.Context, in *pb.UserBase) (out *pb.UserBase, outerr error) { 40 | m := new(User) 41 | out = new(pb.UserBase) 42 | err := copier.Copy(m, in) 43 | if err != nil { 44 | log.Error(err.Error(), ctx) 45 | outerr = status.Error(codes.Internal, err.Error()) 46 | return 47 | } 48 | err = m.Update(ctx) 49 | if err != nil { 50 | outerr = status.Error(codes.InvalidArgument, err.Error()) 51 | return 52 | } 53 | err = copier.Copy(out, m) 54 | if err != nil { 55 | log.Error(err.Error(), ctx) 56 | outerr = status.Error(codes.Internal, err.Error()) 57 | return 58 | } 59 | return 60 | } 61 | 62 | func (s *Server) UserDelete(ctx context.Context, in *pb.UserID) (out *pb.UserID, outerr error) { 63 | m := new(User) 64 | out = new(pb.UserID) 65 | err := copier.Copy(m, in) 66 | if err != nil { 67 | log.Error(err.Error(), ctx) 68 | outerr = status.Error(codes.Internal, err.Error()) 69 | return 70 | } 71 | err = m.Delete(ctx) 72 | if err != nil { 73 | outerr = status.Error(codes.InvalidArgument, err.Error()) 74 | return 75 | } 76 | err = copier.Copy(out, m) 77 | if err != nil { 78 | log.Error(err.Error(), ctx) 79 | outerr = status.Error(codes.Internal, err.Error()) 80 | return 81 | } 82 | return 83 | } 84 | 85 | func (s *Server) UserQueryOne(ctx context.Context, in *pb.UserID) (out *pb.UserBase, outerr error) { 86 | m := new(User) 87 | out = new(pb.UserBase) 88 | err := copier.Copy(m, in) 89 | if err != nil { 90 | log.Error(err.Error(), ctx) 91 | outerr = status.Error(codes.Internal, err.Error()) 92 | return 93 | } 94 | err = m.QueryOne(ctx) 95 | if err != nil { 96 | outerr = status.Error(codes.InvalidArgument, err.Error()) 97 | return 98 | } 99 | err = copier.Copy(out, m) 100 | if err != nil { 101 | log.Error(err.Error(), ctx) 102 | outerr = status.Error(codes.Internal, err.Error()) 103 | return 104 | } 105 | return 106 | } 107 | 108 | func (s *Server) UserQueryAll(ctx context.Context, in *pb.UserAllOption) (*pb.UserAll, error) { 109 | m := new(User) 110 | err := copier.Copy(m, in) 111 | if err != nil { 112 | log.Error(err.Error(), ctx) 113 | return &pb.UserAll{}, status.Error(codes.Internal, err.Error()) 114 | } 115 | ms, page, err := m.QueryAll(ctx) 116 | if err != nil { 117 | return &pb.UserAll{}, status.Error(codes.InvalidArgument, err.Error()) 118 | } 119 | var agt []*pb.UserBase 120 | err = copier.Copy(&agt, ms) 121 | if err != nil { 122 | log.Error(err.Error(), ctx) 123 | return &pb.UserAll{}, status.Error(codes.Internal, err.Error()) 124 | } 125 | _page := new(pb.Page) 126 | err = copier.Copy(_page, &page) 127 | if err != nil { 128 | log.Error(err.Error(), ctx) 129 | return &pb.UserAll{}, status.Error(codes.Internal, err.Error()) 130 | } 131 | return &pb.UserAll{All: agt, Page: _page}, nil 132 | } 133 | -------------------------------------------------------------------------------- /docs/godoc2md/account.md: -------------------------------------------------------------------------------- 1 | # account 2 | `import "/home/meng/workspace/go/istio-micro/srv/account/"` 3 | 4 | * [Overview](#pkg-overview) 5 | * [Imported Packages](#pkg-imports) 6 | * [Index](#pkg-index) 7 | 8 | ## Overview 9 | 10 | ## Imported Packages 11 | 12 | - github.com/asaskevich/govalidator 13 | - github.com/golang/protobuf/ptypes/empty 14 | - github.com/grpc-ecosystem/go-grpc-middleware 15 | - github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing 16 | - github.com/xiaomeng79/go-log 17 | - github.com/xiaomeng79/istio-micro/cinit 18 | - github.com/xiaomeng79/istio-micro/internal/gateway 19 | - github.com/xiaomeng79/istio-micro/internal/sqlupdate 20 | - github.com/xiaomeng79/istio-micro/internal/utils 21 | - github.com/xiaomeng79/istio-micro/internal/wrapper 22 | - github.com/xiaomeng79/istio-micro/srv/account/proto 23 | - github.com/xiaomeng79/istio-micro/version 24 | - google.golang.org/grpc 25 | - google.golang.org/grpc/codes 26 | - google.golang.org/grpc/reflection 27 | - google.golang.org/grpc/status 28 | 29 | ## Index 30 | * [Constants](#pkg-constants) 31 | * [func Run()](#Run) 32 | * [type Account](#Account) 33 | * [func (m \*Account) Add(ctx context.Context) error](#Account.Add) 34 | * [func (m \*Account) QueryOne(ctx context.Context) error](#Account.QueryOne) 35 | * [func (m \*Account) Update(ctx context.Context) error](#Account.Update) 36 | * [type Server](#Server) 37 | * [func (s \*Server) AccountAdd(ctx context.Context, in \*pb.AccountBase) (out \*pb.AccountBase, outerr error)](#Server.AccountAdd) 38 | * [func (s \*Server) AccountQueryOne(ctx context.Context, in \*pb.AccountID) (out \*pb.AccountBase, outerr error)](#Server.AccountQueryOne) 39 | * [func (s \*Server) AccountUpdate(ctx context.Context, in \*pb.AccountUpdateReq) (out \*empty.Empty, outerr error)](#Server.AccountUpdate) 40 | 41 | #### Package files 42 | [account.go](./account.go) [model.go](./model.go) [run.go](./run.go) [versionupdate.go](./versionupdate.go) 43 | 44 | ## Constants 45 | ``` go 46 | const ( 47 | SN = "srv-account"// 定义services名称 48 | ) 49 | ``` 50 | 51 | ## func [Run](./run.go#L21) 52 | ``` go 53 | func Run() 54 | ``` 55 | 56 | ## type [Account](./model.go#L14-L17) 57 | ``` go 58 | type Account struct { 59 | Page utils.Page 60 | // contains filtered or unexported fields 61 | } 62 | ``` 63 | 64 | ### func (\*Account) [Add](./model.go#L62) 65 | ``` go 66 | func (m *Account) Add(ctx context.Context) error 67 | ``` 68 | 添加 ,返回id 69 | 70 | ### func (\*Account) [QueryOne](./model.go#L129) 71 | ``` go 72 | func (m *Account) QueryOne(ctx context.Context) error 73 | ``` 74 | 查询一个 75 | 76 | ### func (\*Account) [Update](./model.go#L92) 77 | ``` go 78 | func (m *Account) Update(ctx context.Context) error 79 | ``` 80 | 修改 81 | 82 | ## type [Server](./account.go#L11) 83 | ``` go 84 | type Server struct{} 85 | ``` 86 | 87 | ### func (\*Server) [AccountAdd](./account.go#L13) 88 | ``` go 89 | func (s *Server) AccountAdd(ctx context.Context, in *pb.AccountBase) (out *pb.AccountBase, outerr error) 90 | ``` 91 | 92 | ### func (\*Server) [AccountQueryOne](./account.go#L65) 93 | ``` go 94 | func (s *Server) AccountQueryOne(ctx context.Context, in *pb.AccountID) (out *pb.AccountBase, outerr error) 95 | ``` 96 | 查询一个 97 | 98 | ### func (\*Server) [AccountUpdate](./account.go#L26) 99 | ``` go 100 | func (s *Server) AccountUpdate(ctx context.Context, in *pb.AccountUpdateReq) (out *empty.Empty, outerr error) 101 | ``` 102 | 103 | - - - 104 | Generated by [godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd) -------------------------------------------------------------------------------- /internal/trace/config.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | 8 | "github.com/xiaomeng79/go-log" 9 | 10 | ot "github.com/opentracing/opentracing-go" 11 | "github.com/uber/jaeger-client-go" 12 | "github.com/uber/jaeger-client-go/transport" 13 | "github.com/uber/jaeger-client-go/transport/zipkin" 14 | zk "github.com/uber/jaeger-client-go/zipkin" 15 | ) 16 | 17 | type holder struct { 18 | closer io.Closer 19 | tracer ot.Tracer 20 | } 21 | 22 | var ( 23 | httpTimeout = 5 * time.Second 24 | poolSpans = jaeger.TracerOptions.PoolSpans(false) 25 | logger = spanLogger{} 26 | ) 27 | 28 | // indirection for testing 29 | type newZipkin func(url string, options ...zipkin.HTTPOption) (*zipkin.HTTPTransport, error) 30 | 31 | // Configure initializes Istio's tracing subsystem.// // You typically call this once at process startup.// Once this call returns, the tracing system is ready to accept data. 32 | func Configure(serviceName string, options *Options) (io.Closer, error) { 33 | return configure(serviceName, options, zipkin.NewHTTPTransport) 34 | } 35 | 36 | func configure(serviceName string, options *Options, nz newZipkin) (io.Closer, error) { 37 | if err := options.Validate(); err != nil { 38 | return nil, err 39 | } 40 | 41 | reporters := make([]jaeger.Reporter, 0, 3) 42 | 43 | sampler, err := jaeger.NewProbabilisticSampler(options.SamplingRate) 44 | if err != nil { 45 | return nil, fmt.Errorf("could not build trace sampler: %v", err) 46 | } 47 | 48 | if options.ZipkinURL != "" { 49 | trans, err := nz(options.ZipkinURL, zipkin.HTTPLogger(logger), zipkin.HTTPTimeout(httpTimeout)) 50 | if err != nil { 51 | return nil, fmt.Errorf("could not build zipkin reporter: %v", err) 52 | } 53 | reporters = append(reporters, jaeger.NewRemoteReporter(trans)) 54 | } 55 | 56 | if options.JaegerURL != "" { 57 | reporters = append(reporters, jaeger.NewRemoteReporter(transport.NewHTTPTransport(options.JaegerURL, transport.HTTPTimeout(httpTimeout)))) 58 | } 59 | 60 | if options.LogTraceSpans { 61 | reporters = append(reporters, logger) 62 | } 63 | 64 | var rep jaeger.Reporter 65 | switch len(reporters) { 66 | case 0: 67 | return holder{}, nil 68 | case 1: 69 | rep = reporters[0] 70 | default: 71 | rep = jaeger.NewCompositeReporter(reporters...) 72 | } 73 | 74 | var tracer ot.Tracer 75 | var closer io.Closer 76 | 77 | if options.ZipkinURL != "" { 78 | zipkinPropagator := zk.NewZipkinB3HTTPHeaderPropagator() 79 | injector := jaeger.TracerOptions.Injector(ot.HTTPHeaders, zipkinPropagator) 80 | extractor := jaeger.TracerOptions.Extractor(ot.HTTPHeaders, zipkinPropagator) 81 | tracer, closer = jaeger.NewTracer(serviceName, sampler, rep, poolSpans, injector, extractor, jaeger.TracerOptions.Gen128Bit(true)) 82 | } else { 83 | tracer, closer = jaeger.NewTracer(serviceName, sampler, rep, poolSpans, jaeger.TracerOptions.Gen128Bit(true)) 84 | } 85 | 86 | // NOTE: global side effect! 87 | ot.SetGlobalTracer(tracer) 88 | 89 | return holder{ 90 | closer: closer, 91 | tracer: tracer, 92 | }, nil 93 | } 94 | 95 | func (h holder) Close() error { 96 | if ot.GlobalTracer() == h.tracer { 97 | ot.SetGlobalTracer(ot.NoopTracer{}) 98 | } 99 | 100 | var err error 101 | if h.closer != nil { 102 | err = h.closer.Close() 103 | } 104 | 105 | return err 106 | } 107 | 108 | type spanLogger struct{} 109 | 110 | // Report implements the Report() method of jaeger.Reporter 111 | func (spanLogger) Report(span *jaeger.Span) { 112 | log.Infof("Reporting span operation:%s,span:%s", span.OperationName(), span.String()) 113 | } 114 | 115 | // Close implements the Close() method of jaeger.Reporter. 116 | func (spanLogger) Close() {} 117 | 118 | // Error implements the Error() method of log.Logger. 119 | func (spanLogger) Error(msg string) { 120 | log.Error(msg) 121 | } 122 | 123 | // Infof implements the Infof() method of log.Logger. 124 | func (spanLogger) Infof(msg string, args ...interface{}) { 125 | log.Infof(msg, args...) 126 | } 127 | -------------------------------------------------------------------------------- /srv/user/proto/google/rpc/status.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. 2 | 3 | syntax = "proto3"; 4 | 5 | package google.rpc; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; 10 | option java_multiple_files = true; 11 | option java_outer_classname = "StatusProto"; 12 | option java_package = "com.google.rpc"; 13 | option objc_class_prefix = "RPC"; 14 | 15 | // The `Status` type defines a logical error model that is suitable for different// programming environments, including REST APIs and RPC APIs. It is used by// [gRPC](https://github.com/grpc). The error model is designed to be:// // - Simple to use and understand for most users// - Flexible enough to meet unexpected needs// // # Overview// // The `Status` message contains three pieces of data: error code, error message,// and error details. The error code should be an enum value of// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The// error message should be a developer-facing English message that helps// developers *understand* and *resolve* the error. If a localized user-facing// error message is needed, put the localized message in the error details or// localize it in the client. The optional error details may contain arbitrary// information about the error. There is a predefined set of error detail types// in the package `google.rpc` that can be used for common error conditions.// // # Language mapping// // The `Status` message is the logical representation of the error model, but it// is not necessarily the actual wire format. When the `Status` message is// exposed in different client libraries and different wire protocols, it can be// mapped differently. For example, it will likely be mapped to some exceptions// in Java, but more likely mapped to some error codes in C.// // # Other uses// // The error model and the `Status` message can be used in a variety of// environments, either with or without APIs, to provide a// consistent developer experience across different environments.// // Example uses of this error model include:// // - Partial errors. If a service needs to return partial errors to the client,// it may embed the `Status` in the normal response to indicate the partial// errors.// // - Workflow errors. A typical workflow has multiple steps. Each step may// have a `Status` message for error reporting.// // - Batch operations. If a client uses batch request and batch response, the// `Status` message should be used directly inside batch response, one for// each error sub-response.// // - Asynchronous operations. If an API call embeds asynchronous operation// results in its response, the status of those operations should be// represented directly using the `Status` message.// // - Logging. If some API errors are stored in logs, the message `Status` could// be used directly after any stripping needed for security/privacy reasons. 16 | message Status { 17 | // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. 18 | int32 code = 1; 19 | 20 | // A developer-facing error message, which should be in English. Any 21 | // user-facing error message should be localized and sent in the 22 | // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. 23 | string message = 2; 24 | 25 | // A list of messages that carry the error details. There is a common set of 26 | // message types for APIs to use. 27 | repeated google.protobuf.Any details = 3; 28 | } 29 | -------------------------------------------------------------------------------- /srv/account/proto/google/rpc/status.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Google Inc.// // Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// // http://www.apache.org/licenses/LICENSE-2.0// // Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. 2 | 3 | syntax = "proto3"; 4 | 5 | package google.rpc; 6 | 7 | import "google/protobuf/any.proto"; 8 | 9 | option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; 10 | option java_multiple_files = true; 11 | option java_outer_classname = "StatusProto"; 12 | option java_package = "com.google.rpc"; 13 | option objc_class_prefix = "RPC"; 14 | 15 | // The `Status` type defines a logical error model that is suitable for different// programming environments, including REST APIs and RPC APIs. It is used by// [gRPC](https://github.com/grpc). The error model is designed to be:// // - Simple to use and understand for most users// - Flexible enough to meet unexpected needs// // # Overview// // The `Status` message contains three pieces of data: error code, error message,// and error details. The error code should be an enum value of// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The// error message should be a developer-facing English message that helps// developers *understand* and *resolve* the error. If a localized user-facing// error message is needed, put the localized message in the error details or// localize it in the client. The optional error details may contain arbitrary// information about the error. There is a predefined set of error detail types// in the package `google.rpc` that can be used for common error conditions.// // # Language mapping// // The `Status` message is the logical representation of the error model, but it// is not necessarily the actual wire format. When the `Status` message is// exposed in different client libraries and different wire protocols, it can be// mapped differently. For example, it will likely be mapped to some exceptions// in Java, but more likely mapped to some error codes in C.// // # Other uses// // The error model and the `Status` message can be used in a variety of// environments, either with or without APIs, to provide a// consistent developer experience across different environments.// // Example uses of this error model include:// // - Partial errors. If a service needs to return partial errors to the client,// it may embed the `Status` in the normal response to indicate the partial// errors.// // - Workflow errors. A typical workflow has multiple steps. Each step may// have a `Status` message for error reporting.// // - Batch operations. If a client uses batch request and batch response, the// `Status` message should be used directly inside batch response, one for// each error sub-response.// // - Asynchronous operations. If an API call embeds asynchronous operation// results in its response, the status of those operations should be// represented directly using the `Status` message.// // - Logging. If some API errors are stored in logs, the message `Status` could// be used directly after any stripping needed for security/privacy reasons. 16 | message Status { 17 | // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. 18 | int32 code = 1; 19 | 20 | // A developer-facing error message, which should be in English. Any 21 | // user-facing error message should be localized and sent in the 22 | // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. 23 | string message = 2; 24 | 25 | // A list of messages that carry the error details. There is a common set of 26 | // message types for APIs to use. 27 | repeated google.protobuf.Any details = 3; 28 | } 29 | -------------------------------------------------------------------------------- /srv/user/proto/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package user; 4 | // 参考:https://github.com/chai2010/advanced-go-programming-book/blob/master/ch4-rpc/ch4-06-grpc-ext.md// 参考:https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/proto/examplepb 5 | 6 | import "google/api/annotations.proto";// import "google/protobuf/empty.proto"; 7 | import "protoc-gen-swagger/options/annotations.proto"; 8 | 9 | option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { 10 | info: { 11 | title: "用户"; 12 | version: "1.0"; 13 | contact: { 14 | name: "xiaomeng79"; 15 | url: "https://github.com/xiaomeng79"; 16 | email: "mengmeng79@yeah.net"; 17 | }; 18 | }; 19 | host: "127.0.0.1:9999"; 20 | // base_path: "/backend/v1"; 21 | schemes: HTTP; 22 | consumes: "application/json"; 23 | produces: "application/json"; 24 | security_definitions: { 25 | security: { 26 | key: "ApiKeyAuth"; 27 | value: { 28 | type: TYPE_API_KEY; 29 | in: IN_HEADER; 30 | name: "X-API-Key"; 31 | } 32 | } 33 | } 34 | security: { 35 | security_requirement: { 36 | key: "ApiKeyAuth"; 37 | value: {}; 38 | } 39 | } 40 | responses: { 41 | key: "403"; 42 | value: { 43 | description: "没权限"; 44 | } 45 | } 46 | }; 47 | 48 | 49 | // 用户服务 50 | service UserService { 51 | // 用户添加 52 | rpc UserAdd(UserBase) returns (UserBase) { 53 | option (google.api.http) = { 54 | post: "/user" 55 | body:"*" 56 | }; 57 | } 58 | // 用户更新 59 | rpc UserUpdate(UserBase) returns (UserBase) { 60 | option (google.api.http) = { 61 | put: "/user" 62 | body:"*" 63 | }; 64 | } 65 | // 用户删除 66 | rpc UserDelete(UserID) returns (UserID) { 67 | option (google.api.http) = { 68 | delete: "/user/{id}" 69 | }; 70 | } 71 | // 用户查询一个 72 | rpc UserQueryOne(UserID) returns (UserBase) { 73 | option (google.api.http) = { 74 | get: "/user/{id}" 75 | response_body:"*" 76 | }; 77 | } 78 | // 用户查询全部 79 | rpc UserQueryAll(UserAllOption) returns (UserAll) { 80 | option (google.api.http) = { 81 | get: "/user" 82 | response_body:"*" 83 | }; 84 | } 85 | } 86 | // 用户性别 87 | enum UserSex { 88 | DEFAULT = 0; 89 | MEN = 1; 90 | WOMEN = 2; 91 | } 92 | // 注入标签: https://github.com/favadi/protoc-go-inject-tag 93 | // 用户基本信息 94 | message UserBase { 95 | // @inject_tag: db:"id" 96 | int64 id = 1 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"用户id"}]; 97 | // @inject_tag: db:"user_name" valid:"required~用户名称必须存在" 98 | string user_name = 2 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"用户名称"}]; 99 | // @inject_tag: db:"iphone" valid:"required~手机号必须存在" 100 | string iphone = 3 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"手机号"}]; 101 | // @inject_tag: db:"password" 102 | string password = 4 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"密码"}]; 103 | // @inject_tag: db:"sex" 104 | UserSex sex = 5 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"性别"}]; 105 | }// 用户ID 106 | message UserID { 107 | // 用户id 108 | int64 id = 1; 109 | } 110 | // 用户全部选项 111 | message UserAllOption { 112 | // 用户性别 113 | UserSex sex = 1; 114 | // 页数 115 | Page page = 2; 116 | // 用户名称 117 | string user_name = 3; 118 | } 119 | // 用户全部 120 | message UserAll { 121 | // 用户信息 122 | repeated UserBase all = 1; 123 | // 页数 124 | Page page = 2; 125 | } 126 | // 空消息 127 | message Null {} 128 | // 分页 129 | message Page { 130 | // 页 131 | int64 page_index = 1; 132 | // 每页大小 133 | int64 page_size = 2; 134 | // 总页数 135 | int64 page_total = 3; 136 | // 条数 137 | int64 count = 4; 138 | // 总条数 139 | int64 total = 5; 140 | } -------------------------------------------------------------------------------- /internal/gateway/server.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" 8 | "github.com/xiaomeng79/go-log" 9 | ) 10 | 11 | // 参考:https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/gateway 12 | const ( 13 | DefaultAddr = ":8888" // 默认地址 14 | DefaultGrpcAddr = ":5001" // 默认grpc地址 15 | DefaultGrpcNetwork = "tcp" // 默认grpc 16 | DefaultSwaggerDir = "/swagger" 17 | ) 18 | 19 | // Endpoint describes a gRPC endpoint 20 | type Endpoint struct { 21 | Network, Addr string 22 | } 23 | 24 | // Options is a set of options to be passed to Run 25 | type Options struct { 26 | // Addr is the address to listen 27 | Addr string 28 | 29 | // GRPCServer defines an endpoint of a gRPC service 30 | GRPCServer Endpoint 31 | 32 | // SwaggerDir is a path to a directory from which the server 33 | // serves swagger specs. 34 | SwaggerDir string 35 | 36 | // Mux is a list of options to be passed to the grpc-gateway multiplexer 37 | Mux []gwruntime.ServeMuxOption 38 | 39 | // 注册 40 | Handles []regHandle 41 | } 42 | 43 | // 网关参数设置 44 | type Option func(*Options) 45 | 46 | // 设置监听地址 47 | func WithAddr(addr string) Option { 48 | return func(opts *Options) { 49 | opts.Addr = addr 50 | } 51 | } 52 | 53 | // 设置grpc服务地址 54 | func WithGRPCServer(network, addr string) Option { 55 | return func(opts *Options) { 56 | opts.GRPCServer = Endpoint{ 57 | Addr: addr, 58 | Network: network, 59 | } 60 | } 61 | } 62 | 63 | // 设置swagger目录 64 | func WithSwaggerDir(dir string) Option { 65 | return func(opts *Options) { 66 | opts.SwaggerDir = dir 67 | } 68 | } 69 | 70 | // 设置mux 71 | func WithMuxOption(mux ...gwruntime.ServeMuxOption) Option { 72 | return func(opts *Options) { 73 | opts.Mux = mux 74 | } 75 | } 76 | 77 | // 设置Handles 78 | func WithHandle(handle ...regHandle) Option { 79 | return func(opts *Options) { 80 | opts.Handles = handle 81 | } 82 | } 83 | 84 | // 开启一个网关服务 85 | /* go gateway.Run( 86 | ctx, 87 | gateway.WithAddr(":8888"), 88 | gateway.WithGRPCServer("tcp", ":5001"), 89 | gateway.WithSwaggerDir("/swagger"), 90 | gateway.WithHandle(pb.RegisterRbacServiceHandler), 91 | ) 92 | */ 93 | // Run starts a HTTP server and blocks while running if successful.// The server will be shutdown when "ctx" is canceled. 94 | func Run(ctx context.Context, options ...Option) error { 95 | ctx, cancel := context.WithCancel(ctx) 96 | defer cancel() 97 | // 初始化参数 98 | opts := &Options{ 99 | Addr: DefaultAddr, 100 | GRPCServer: Endpoint{ 101 | Network: DefaultGrpcNetwork, 102 | Addr: DefaultGrpcAddr, 103 | }, 104 | SwaggerDir: DefaultSwaggerDir, 105 | Mux: make([]gwruntime.ServeMuxOption, 0), 106 | Handles: make([]regHandle, 0), 107 | } 108 | for _, f := range options { 109 | f(opts) 110 | } 111 | conn, err := dial(ctx, opts.GRPCServer.Network, opts.GRPCServer.Addr) 112 | if err != nil { 113 | return err 114 | } 115 | go func() { 116 | <-ctx.Done() 117 | if err = conn.Close(); err != nil { 118 | log.Errorf("Failed to close a client connection to the gRPC server: %v", err) 119 | } 120 | }() 121 | 122 | mux := http.NewServeMux() 123 | mux.HandleFunc("/swagger/", swaggerServer(opts.SwaggerDir)) 124 | mux.HandleFunc("/healthz", healthzServer(conn)) 125 | 126 | gw, err := newGateway(ctx, conn, opts.Mux, opts.Handles) 127 | if err != nil { 128 | return err 129 | } 130 | mux.Handle("/", gw) 131 | 132 | s := &http.Server{ 133 | Addr: opts.Addr, 134 | Handler: allowCORS(mux), 135 | } 136 | go func() { 137 | <-ctx.Done() 138 | log.Infof("Shutting down the http server") 139 | if err := s.Shutdown(context.Background()); err != nil { 140 | log.Errorf("Failed to shutdown http server: %v", err) 141 | } 142 | }() 143 | 144 | log.Infof("Starting listening at %s", opts.Addr) 145 | if err := s.ListenAndServe(); err != http.ErrServerClosed { 146 | log.Errorf("Failed to listen and serve: %v", err) 147 | return err 148 | } 149 | return nil 150 | } 151 | -------------------------------------------------------------------------------- /internal/api/errors.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "strconv" 7 | "strings" 8 | 9 | "github.com/labstack/echo" 10 | "github.com/xiaomeng79/go-log" 11 | "google.golang.org/grpc/codes" 12 | "google.golang.org/grpc/status" 13 | ) 14 | 15 | // 定义错误信息 16 | 17 | type ErrorNo int64 18 | 19 | func (e ErrorNo) String() string { 20 | return strconv.FormatInt(int64(e), 10) 21 | } 22 | 23 | const ( 24 | // 成功 25 | Success ErrorNo = 0 26 | // 请求 27 | ReqPathError = 10001 28 | ReqVersionNoExist = 10002 29 | ReqInterfaceNoExist = 10003 30 | ReqCommandNoExist = 10004 31 | ReqInterfaceNoSupport = 10005 32 | ReqNoAllow = 10006 33 | ReqTokenError = 10007 34 | 35 | // 公共 36 | CommonPageError = 20001 // 分页错误 37 | CommonParamError = 20002 // 参数错误 38 | CommonParamConvertError = 20003 // 参数转换错误 39 | CommonSignError = 20004 // 签名错误 40 | CommonAppError = 20008 // app错误 41 | 42 | // 业务参数 43 | BusParamError = 30001 // 参数错误 44 | BusParamConvertError = 30002 // 参数转换错误 45 | 46 | // 权限 47 | 48 | // 服务端处理异常 49 | ServiceError = 50001 50 | ) 51 | 52 | var ReturnMsg = map[ErrorNo]string{ 53 | // 成功 54 | Success: "success", 55 | // 请求 56 | ReqPathError: "请求路径错误", 57 | ReqVersionNoExist: "请求版本不存在", 58 | ReqInterfaceNoExist: "请求接口不存在", 59 | ReqCommandNoExist: "请求命令不存在", 60 | ReqInterfaceNoSupport: "接口在当前请求命令中未被支持", 61 | ReqNoAllow: "请求不允许", 62 | ReqTokenError: "Token不正确,请重新获取", 63 | 64 | // 公共 65 | CommonPageError: "分页错误", // 分页错误 66 | CommonParamError: "公共参数错误", // 参数错误 67 | CommonParamConvertError: "公共参数转换错误", // 参数转换错误 68 | CommonSignError: "公共参数签名错误", // 签名错误 69 | CommonAppError: "商户账户不存在或不可用", // 70 | 71 | // 业务参数 72 | BusParamError: "业务参数错误", // 参数错误 73 | BusParamConvertError: "业务参数转换错误", // 参数转换错误 74 | 75 | // 服务端处理异常 76 | ServiceError: "服务端处理异常", 77 | } 78 | 79 | /** 80 | 异常错误公共 81 | */ 82 | func errCommon(code ErrorNo, errmsg ...string) interface{} { 83 | return map[string]interface{}{ 84 | "code": code.String(), 85 | "message": ReturnMsg[code] + strings.Join(errmsg, " "), 86 | } 87 | } 88 | 89 | // RPC错误处理 90 | func RPCErr(c echo.Context, err error) error { 91 | st := status.Convert(err) 92 | switch st.Code() { 93 | case codes.InvalidArgument: 94 | return c.JSON(http.StatusBadRequest, errCommon(BusParamError, ":", st.Message())) 95 | case codes.PermissionDenied: 96 | return c.JSON(http.StatusUnauthorized, errCommon(BusParamError, ":", st.Message())) 97 | default: 98 | return c.JSON(http.StatusInternalServerError, errCommon(ServiceError)) 99 | } 100 | } 101 | 102 | /** 103 | 正常返回 104 | */ 105 | func HandleSuccess(c echo.Context, i ...interface{}) error { 106 | resp := make(map[string]interface{}) 107 | resp["code"] = Success 108 | resp["message"] = ReturnMsg[Success] 109 | switch len(i) { 110 | case 1: 111 | resp["data"] = i 112 | case 2: 113 | resp["data"] = i[0] 114 | resp["page"] = i[1] 115 | default: 116 | } 117 | return c.JSON(http.StatusOK, resp) 118 | } 119 | 120 | func HandleSuccessReq(ctx context.Context, c echo.Context, r *ReqParam, v interface{}) error { 121 | _r, err := r.R(v) 122 | if err != nil { 123 | log.Error(err.Error(), ctx) 124 | return HandleError(c, http.StatusInternalServerError, err.Error()) 125 | } 126 | return c.JSON(http.StatusOK, _r) 127 | } 128 | 129 | /** 130 | 错误返回 131 | */ 132 | func HandleError(c echo.Context, errcode ErrorNo, errmsg ...string) error { 133 | co := int64(errcode) 134 | var code int 135 | switch { 136 | case co == 0: 137 | code = http.StatusOK 138 | case co < 20000: 139 | code = http.StatusUnauthorized 140 | case co < 50000: 141 | code = http.StatusBadRequest 142 | case co < 60000: 143 | code = http.StatusInternalServerError 144 | default: 145 | code = http.StatusInternalServerError 146 | } 147 | return c.JSON(code, map[string]interface{}{ 148 | "code": errcode.String(), 149 | "message": ReturnMsg[errcode] + strings.Join(errmsg, " "), 150 | }) 151 | } 152 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #定义变量 2 | GOPROXY=https://goproxy.io 3 | GO111MODULE=on 4 | #GOTHIRDPKG=${HOME}/gopkg/third 5 | VERSION=$(shell git describe --abbrev=0 --tags) 6 | COMMIT=$(shell git rev-parse --short HEAD) 7 | 8 | #project:game prize pusher socket 9 | #type:api srv web 10 | 11 | .PHONY : ver 12 | ver : 13 | 14 | @echo "执行环境变量" 15 | @chmod +x ./scripts/.variables.sh && ./scripts/.variables.sh 16 | @echo "完成环境变量" 17 | 18 | 19 | .PHONY : install 20 | install : 21 | 22 | @echo "安装环境开始" 23 | @chmod +x ./scripts/install.sh && ./scripts/install.sh ${method} 24 | @echo "安装环境结束" 25 | 26 | .PHONY : fmt 27 | fmt : 28 | @echo "格式化代码" 29 | @gofmt -l -w ./ 30 | 31 | .PHONY : vendor 32 | vendor : 33 | @echo "创建vendor" 34 | @go mod vendor 35 | @echo "结束vendor" 36 | 37 | .PHONY : test 38 | test : vendor 39 | @echo "代码测试[覆盖率]" 40 | @go test -mod=vendor -race -cover -coverprofile=coverage.txt -covermode=atomic ./... 41 | 42 | .PHONY : onlinetest 43 | onlinetest : 44 | @echo "代码测试[覆盖率]" 45 | @go test -race -cover -coverprofile=coverage.txt -covermode=atomic ./... 46 | 47 | #代码检查 48 | .PHONY : check 49 | check : 50 | @echo "代码检查" 51 | @echo "代码静态检查开始" 52 | @go vet ./... 53 | @echo "代码静态检查结束" 54 | @chmod +x ./scripts/check.sh && ./scripts/check.sh 55 | # @chmod +x ./scripts/check.sh && ./scripts/check.sh | tee check.log 56 | 57 | 58 | .PHONY : build 59 | build : proto builddata dockerfile vendor 60 | @echo "部分编译开始:"$(project)_$(type) 61 | @chmod +x ./scripts/build.sh && ./scripts/build.sh build $(type) $(project) 62 | @echo "部分编译结束" 63 | 64 | 65 | 66 | .PHONY : allbuild 67 | allbuild : proto builddata alldockerfile vendor 68 | 69 | @echo "全部编译开始" 70 | @chmod +x ./scripts/build.sh && ./scripts/build.sh allbuild 71 | @echo "全部编译结束" 72 | 73 | 74 | 75 | #生成pb文件 76 | 77 | .PHONY : proto 78 | proto : 79 | 80 | @echo "生成proto开始" 81 | @chmod +x ./scripts/proto.sh && ./scripts/proto.sh 82 | @echo "生成proto结束" 83 | 84 | #生成dockerfile 85 | .PHONY : dockerfile 86 | dockerfile : 87 | 88 | @echo "部分生成dockerfile开始" 89 | @chmod +x ./scripts/dockerfile.sh && ./scripts/dockerfile.sh df $(type) $(project) 90 | @echo "部分生成Dockerfile结束" 91 | 92 | 93 | .PHONY : alldockerfile 94 | alldockerfile : 95 | 96 | @echo "全部生成dockerfile开始" 97 | @chmod +x ./scripts/dockerfile.sh && ./scripts/dockerfile.sh alldf 98 | @echo "全部生成Dockerfile结束" 99 | 100 | 101 | #compose命令 bin:up dup stop restart kill rm ps 102 | .PHONY : compose 103 | compose : 104 | @chmod +x ./scripts/docker-compose.sh && ./scripts/docker-compose.sh $(bin) 105 | 106 | .PHONY : builddata 107 | builddata : 108 | @echo "打包静态数据" 109 | @chmod +x ./scripts/builddata.sh && ./scripts/builddata.sh 110 | 111 | #编辑k8s配置 112 | .PHONY : k8sconfig 113 | k8sconfig : 114 | 115 | @echo "配置k8s" 116 | @chmod +x ./scripts/k8sconf.sh && ./scripts/k8sconf.sh 117 | # 代码风格检查 118 | .PHONY : lint 119 | lint : 120 | @golangci-lint run -v ./... 121 | 122 | 123 | #pprof性能分析 124 | .PHONY : pprofon 125 | pprofon : 126 | @chmod +x ./scripts/pprof.sh && ./scripts/pprof.sh pprofon $(type) $(project) 127 | 128 | #pprof性能分析 129 | .PHONY : pprofoff 130 | pprofoff : 131 | @chmod +x ./scripts/pprof.sh && ./scripts/pprof.sh pprofoff $(type) $(project) 132 | 133 | #提交代码 134 | .PHONY : push 135 | push : fmt check test 136 | git add -A 137 | git commit -m $(msg) 138 | git push origin master 139 | 140 | #清理 141 | .PHONY : clean 142 | clean : 143 | @git clean -dxf -e .idea 144 | 145 | #发行版本 146 | 147 | #查看下一个版本号 148 | next-version : 149 | @chmod +x ./scripts/version.sh && ./scripts/version.sh 150 | 151 | #使用godoc生成markdown文档[godoc2ghmd](https://github.com/GandalfUK/godoc2ghmd) 152 | gendoc : 153 | godoc2ghmd `pwd`/srv/account/ > `pwd`/docs/godoc2md/account.md 154 | godoc2ghmd `pwd`/srv/user/ > `pwd`/docs/godoc2md/user.md 155 | 156 | #清理没用的docker镜像 157 | docker-clean: 158 | docker images 159 | docker image prune --force 160 | 161 | docker-kill: 162 | docker kill `docker ps -q` || true 163 | 164 | docker-remove: 165 | docker rm --force `docker ps -a -q` || true 166 | docker rmi --force `docker images -q` || true 167 | 168 | # 使用goreleaser编译项目 169 | .PHONY : release 170 | release: 171 | goreleaser --snapshot --skip-publish --rm-dist 172 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source scripts/.variables.sh 4 | set -xe 5 | 6 | #定义变量 7 | GOPROXY=${GOPROXY:-"https://goproxy.io"} 8 | soft_dir=${HOME:-"/tmp"} 9 | go_version=${go_version:-"1.13.1"} 10 | protoc_version=${protoc_version:-"3.6.1"} 11 | protoc_include_path=${protoc_include_path:-"${soft_dir}/protoc-${protoc_version}-linux-x86_64/include"} 12 | cloc_version=${cloc_version:-"1.76"} 13 | GOPATH=${GOPATH:-${HOME}"/go_path"} 14 | cmd_path=${cmd_path:-"${GOPATH}/bin"} 15 | 16 | #go 17 | go_install(){ 18 | echo "安装golang环境 go"${go_version} && \ 19 | mkdir -p ${soft_dir} && cd ${soft_dir} && \ 20 | wget -c https://dl.google.com/go/go${go_version}.linux-amd64.tar.gz && \ 21 | tar -xzvf go${go_version}.linux-amd64.tar.gz && \ 22 | go version && \ 23 | go env -w GOPROXY=${GOPROXY},direct && \ 24 | go env -w GOPATH=${GOPATH} && \ 25 | go env -w GO111MODULE=auto 26 | } 27 | 28 | #圈复杂分析 29 | cloc_install(){ 30 | #安装cloc 31 | echo "安装代码统计工具 cloc" && \ 32 | mkdir -p ${soft_dir} && cd ${soft_dir} && \ 33 | wget -c https://github.com/AlDanial/cloc/archive/v${cloc_version}.zip && \ 34 | unzip v${cloc_version}.zip && \ 35 | mv ${soft_dir}/cloc-${cloc_version}/cloc ${cmd_path} || { echo "cloc文件已经存在"; } && \ 36 | echo "cloc 的版本是:" && cloc --version 37 | 38 | } 39 | 40 | protoc_install(){ 41 | echo "安装protobuf工具 " && \ 42 | mkdir -p ${soft_dir} && cd ${soft_dir} && \ 43 | wget -c https://github.com/protocolbuffers/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-linux-x86_64.zip && \ 44 | unzip protoc-${protoc_version}-linux-x86_64.zip -d ./protoc-${protoc_version}-linux-x86_64 && \ 45 | mv ${soft_dir}/protoc-${protoc_version}-linux-x86_64/bin/protoc ${cmd_path} && \ 46 | echo "protoc 的版本是:" && protoc --version 47 | } 48 | 49 | golangci-lint(){ 50 | echo "安装golangci-lint工具" 51 | } 52 | 53 | go_plug(){ 54 | cd ${GOPATH} && export GOPROXY=https://goproxy.io && export GO111MODULE=off && export GOPATH=${GOPATH} && \ 55 | echo "GOPATH为:"${GOPATH} && \ 56 | echo "安装 protobuf golang插件 protoc-gen-go protoc-gen-grpc-gateway protoc-gen-swagger protoc-go-inject-tag" && \ 57 | echo "大概耗时30分钟" && \ 58 | mkdir -p ${GOPATH}/src/golang && cd ${GOPATH}/src/golang && git clone https://github.com/golang/protobuf.git --depth 1 59 | mkdir -p ${GOPATH}/src/google.golang.org && cd ${GOPATH}/src/google.golang.org && \ 60 | git clone https://github.com/google/go-genproto.git --depth 1 && mv go-genproto/ genproto/ && \ 61 | cd ${GOPATH} && \ 62 | go get github.com/golang/protobuf/protoc-gen-go && \ 63 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway && \ 64 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger && \ 65 | go get github.com/favadi/protoc-go-inject-tag && \ 66 | go get github.com/golangci/golangci-lint/cmd/golangci-lint && \ 67 | echo "安装gocyclo圈复杂度计算工具" && \ 68 | go get github.com/fzipp/gocyclo && \ 69 | echo "安装打包静态文件工具" && \ 70 | go get github.com/rakyll/statik && \ 71 | echo "安装go-torch" && \ 72 | go get github.com/uber/go-torch && \ 73 | cd ${GOPATH}/src/github.com/uber/go-torch && \ 74 | `git clone https://github.com/brendangregg/FlameGraph.git` || { echo "FlameGraph已经存在"; } 75 | } 76 | 77 | # 安装一些依赖工具到GOPATH 78 | tool_install(){ 79 | cd ${GOPATH} 80 | #代码风格审查 81 | go get github.com/golangci/golangci-lint/cmd/golangci-lint 82 | go get github.com/golang/protobuf/protoc-gen-go 83 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway 84 | go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger 85 | go get github.com/favadi/protoc-go-inject-tag 86 | go get github.com/rakyll/statik 87 | } 88 | 89 | # 安装依赖 90 | case $1 in 91 | tool) echo "依赖工具安装" 92 | protoc_install 93 | tool_install 94 | ;; 95 | go) echo "安装go程序" 96 | go_install 97 | ;; 98 | other) 99 | echo "安装其他工具" 100 | cloc_install 101 | ;; 102 | alltool) 103 | echo "全部工具安装" 104 | go_plug 105 | ;; 106 | *) 107 | echo "安装go程序和依赖工具" 108 | go_install 109 | protoc_install 110 | tool_install 111 | ;; 112 | esac 113 | -------------------------------------------------------------------------------- /srv/account/proto/account.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package account; 4 | // 参考:https://github.com/chai2010/advanced-go-programming-book/blob/master/ch4-rpc/ch4-06-grpc-ext.md// 参考:https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/proto/examplepb 5 | 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | import "protoc-gen-swagger/options/annotations.proto"; 9 | 10 | option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { 11 | info: { 12 | title: "账户"; 13 | version: "1.0"; 14 | contact: { 15 | name: "xiaomeng79"; 16 | url: "https://github.com/xiaomeng79"; 17 | email: "mengmeng79@yeah.net"; 18 | }; 19 | }; 20 | host: "127.0.0.1:9997"; 21 | // base_path: "/backend/v1"; 22 | schemes: HTTP; 23 | consumes: "application/json"; 24 | produces: "application/json"; 25 | security_definitions: { 26 | security: { 27 | key: "ApiKeyAuth"; 28 | value: { 29 | type: TYPE_API_KEY; 30 | in: IN_HEADER; 31 | name: "X-API-Key"; 32 | } 33 | } 34 | } 35 | security: { 36 | security_requirement: { 37 | key: "ApiKeyAuth"; 38 | value: {}; 39 | } 40 | } 41 | responses: { 42 | key: "403"; 43 | value: { 44 | description: "没权限"; 45 | } 46 | } 47 | }; 48 | 49 | 50 | // 账户服务 51 | service AccountService { 52 | // 账户添加 53 | rpc AccountAdd(AccountBase) returns (AccountBase) { 54 | option (google.api.http) = { 55 | post: "/account" 56 | body:"*" 57 | }; 58 | } 59 | // 账户更新 60 | rpc AccountUpdate(AccountUpdateReq) returns (google.protobuf.Empty) { 61 | option (google.api.http) = { 62 | put: "/account/{id}" 63 | body:"*" 64 | }; 65 | } 66 | // // 账户删除// rpc AccountDelete(AccountID) returns (AccountID) {// option (google.api.http) = {// delete: "/Account/{id}"// };// } 67 | 68 | // 账户查询一个 69 | rpc AccountQueryOne(AccountID) returns (AccountBase) { 70 | option (google.api.http) = { 71 | get: "/account/{id}" 72 | response_body:"*" 73 | }; 74 | } 75 | } 76 | 77 | 78 | // 注入标签: https://github.com/favadi/protoc-go-inject-tag 79 | // 账户基本信息 80 | message AccountBase { 81 | // @inject_tag: db:"id" 82 | int64 id = 1 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"账户id"}]; 83 | // @inject_tag: db:"user_id" valid:"required~用户id必须存在" 84 | int64 user_id = 2 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"用户id"}]; 85 | // 账户级别 86 | enum Level { 87 | LevelDefault = 0; 88 | LevelOne = 1; 89 | LevelTwo = 2; 90 | LevelThree = 3; 91 | } 92 | // @inject_tag: db:"account_level" valid:"required~账户级别必须存在" 93 | Level account_level = 3 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"账户级别"}]; 94 | // @inject_tag: db:"balance" 95 | float balance = 4 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"余额"}]; 96 | // 账户状态 97 | enum Status { 98 | StatusDefault = 0; 99 | // 正常 100 | StatusNormal = 1; 101 | // 冻结 102 | StatusFrozen = 2; 103 | } 104 | // @inject_tag: db:"account_status" valid:"required~账户状态必须存在" 105 | Status account_status = 5 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"账户状态"}]; 106 | }// 账户ID 107 | message AccountID { 108 | // 账户id 109 | int64 id = 1 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"账户id" read_only:true}]; 110 | } 111 | // 账户更新请求 112 | message AccountUpdateReq { 113 | // 账户id 114 | int64 id = 1 [(grpc.gateway.protoc_gen_swagger.options.openapiv2_field) = {title:"账户id" read_only:true}]; 115 | // 账户余额 116 | float balance = 2; 117 | } 118 | 119 | // 账户全部 120 | message AccountAll { 121 | // 账户信息 122 | repeated AccountBase all = 1; 123 | // 页数 124 | Page page = 2; 125 | } 126 | // 空消息 127 | message Null {} 128 | // 分页 129 | message Page { 130 | // 页 131 | int64 page_index = 1; 132 | // 每页大小 133 | int64 page_size = 2; 134 | // 总页数 135 | int64 page_total = 3; 136 | // 条数 137 | int64 count = 4; 138 | // 总条数 139 | int64 total = 5; 140 | } -------------------------------------------------------------------------------- /api/backend/user.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/xiaomeng79/istio-micro/internal/api" 7 | "github.com/xiaomeng79/istio-micro/internal/jwt" 8 | "github.com/xiaomeng79/istio-micro/internal/utils" 9 | pb "github.com/xiaomeng79/istio-micro/srv/user/proto" 10 | 11 | "github.com/labstack/echo" 12 | "github.com/xiaomeng79/go-log" 13 | "github.com/xiaomeng79/go-utils/crypto" 14 | ) 15 | 16 | // 添加 17 | func UserAdd(c echo.Context) error { 18 | ctx := c.Request().Context() 19 | // 解析请求参数 20 | _req := new(pb.UserBase) 21 | err := c.Bind(&_req) 22 | if err != nil { 23 | // 解析返回的错误信息 24 | log.Error(err.Error(), ctx) 25 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 26 | } 27 | _rsp, err := UserClient.UserAdd(ctx, _req) 28 | if err != nil { 29 | // 解析返回的错误信息 30 | log.Error(err.Error(), ctx) 31 | return api.RPCErr(c, err) 32 | } 33 | return api.HandleSuccess(c, _rsp) 34 | } 35 | 36 | // 修改 37 | func UserUpdate(c echo.Context) error { 38 | ctx := c.Request().Context() 39 | // 解析请求参数 40 | _req := new(pb.UserBase) 41 | err := c.Bind(&_req) 42 | if err != nil { 43 | // 解析返回的错误信息 44 | log.Error(err.Error(), ctx) 45 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 46 | } 47 | id, err := utils.S2ID(c.Param("id")) 48 | if err != nil { 49 | log.Error(err.Error(), ctx) 50 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 51 | } 52 | _req.Id = id 53 | _rsp, err := UserClient.UserUpdate(ctx, _req) 54 | if err != nil { 55 | // 解析返回的错误信息 56 | log.Error(err.Error(), ctx) 57 | return api.RPCErr(c, err) 58 | } 59 | return api.HandleSuccess(c, _rsp) 60 | } 61 | 62 | // 删除 63 | func UserDelete(c echo.Context) error { 64 | ctx := c.Request().Context() 65 | // 解析请求参数 66 | _req := new(pb.UserID) 67 | id, err := utils.S2ID(c.Param("id")) 68 | if err != nil { 69 | log.Error(err.Error(), ctx) 70 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 71 | } 72 | 73 | _req.Id = id 74 | _rsp, err := UserClient.UserDelete(ctx, _req) 75 | if err != nil { 76 | // 解析返回的错误信息 77 | log.Error(err.Error(), ctx) 78 | return api.RPCErr(c, err) 79 | } 80 | return api.HandleSuccess(c, _rsp) 81 | } 82 | 83 | // 查询一个 84 | func UserQueryOne(c echo.Context) error { 85 | ctx := c.Request().Context() 86 | // 解析请求参数 87 | _req := new(pb.UserID) 88 | id, err := utils.S2ID(c.Param("id")) 89 | if err != nil { 90 | log.Error(err.Error(), ctx) 91 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 92 | } 93 | _req.Id = id 94 | _rsp, err := UserClient.UserQueryOne(ctx, _req) 95 | if err != nil { 96 | // 解析返回的错误信息 97 | log.Error(err.Error(), ctx) 98 | return api.RPCErr(c, err) 99 | } 100 | return api.HandleSuccess(c, _rsp) 101 | } 102 | 103 | // 查询全部 104 | func UserQueryAll(c echo.Context) error { 105 | ctx := c.Request().Context() 106 | 107 | pageIndex, err := utils.S2N(c.QueryParam("page_index")) 108 | if err != nil { 109 | log.Error(err.Error(), ctx) 110 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 111 | } 112 | pageSize, err := utils.S2N(c.QueryParam("page_size")) 113 | if err != nil { 114 | log.Error(err.Error(), ctx) 115 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 116 | } 117 | 118 | _page := new(pb.Page) 119 | _page.PageSize = pageSize 120 | _page.PageIndex = pageIndex 121 | 122 | _req := new(pb.UserAllOption) 123 | _req.Page = _page 124 | _rsp, err := UserClient.UserQueryAll(ctx, _req) 125 | if err != nil { 126 | // 解析返回的错误信息 127 | log.Error(err.Error(), ctx) 128 | return api.RPCErr(c, err) 129 | } 130 | return api.HandleSuccess(c, _rsp.All, _rsp.Page) 131 | } 132 | 133 | const ( 134 | UserName = "root" 135 | ) 136 | 137 | type User struct { 138 | UserName string `json:"user_name"` 139 | Password string `json:"password"` 140 | } 141 | 142 | // 登录 143 | func Login(c echo.Context) error { 144 | ctx := c.Request().Context() 145 | user := new(User) 146 | err := c.Bind(user) 147 | if err != nil { 148 | // 解析返回的错误信息 149 | log.Error(err.Error(), ctx) 150 | return api.HandleError(c, api.BusParamConvertError, err.Error()) 151 | } 152 | if user.UserName != UserName || !strings.EqualFold(crypto.MD5(user.Password), "394810afe2fcb9a8210b80300d7ccc7a") { 153 | return api.HandleError(c, api.ReqNoAllow, "用户名或者密码不正确") 154 | } 155 | j := new(jwt.Msg) 156 | j.UserName = user.UserName 157 | token, err := jwt.Encode(*j) 158 | if err != nil { 159 | // 解析返回的错误信息 160 | log.Error(err.Error(), ctx) 161 | return api.HandleError(c, api.ServiceError, err.Error()) 162 | } 163 | t := make(map[string]string) 164 | t["token"] = token 165 | return api.HandleSuccess(c, t) 166 | } 167 | -------------------------------------------------------------------------------- /srv/account/proto/account.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "账户", 5 | "version": "1.0", 6 | "contact": { 7 | "name": "xiaomeng79", 8 | "url": "https://github.com/xiaomeng79", 9 | "email": "mengmeng79@yeah.net" 10 | } 11 | }, 12 | "host": "127.0.0.1:9997", 13 | "schemes": [ 14 | "http" 15 | ], 16 | "consumes": [ 17 | "application/json" 18 | ], 19 | "produces": [ 20 | "application/json" 21 | ], 22 | "paths": { 23 | "/account": { 24 | "post": { 25 | "summary": "账户添加", 26 | "operationId": "AccountAdd", 27 | "responses": { 28 | "200": { 29 | "description": "A successful response.", 30 | "schema": { 31 | "$ref": "#/definitions/accountAccountBase" 32 | } 33 | }, 34 | "403": { 35 | "description": "没权限", 36 | "schema": {} 37 | } 38 | }, 39 | "parameters": [ 40 | { 41 | "name": "body", 42 | "in": "body", 43 | "required": true, 44 | "schema": { 45 | "$ref": "#/definitions/accountAccountBase" 46 | } 47 | } 48 | ], 49 | "tags": [ 50 | "AccountService" 51 | ] 52 | } 53 | }, 54 | "/account/{id}": { 55 | "get": { 56 | "summary": "账户查询一个", 57 | "operationId": "AccountQueryOne", 58 | "responses": { 59 | "200": { 60 | "description": "A successful response.", 61 | "schema": { 62 | "$ref": "#/definitions/accountAccountBase" 63 | } 64 | }, 65 | "403": { 66 | "description": "没权限", 67 | "schema": {} 68 | } 69 | }, 70 | "parameters": [ 71 | { 72 | "name": "id", 73 | "description": "账户id", 74 | "in": "path", 75 | "required": true, 76 | "type": "string", 77 | "format": "int64" 78 | } 79 | ], 80 | "tags": [ 81 | "AccountService" 82 | ] 83 | }, 84 | "put": { 85 | "summary": "账户更新", 86 | "operationId": "AccountUpdate", 87 | "responses": { 88 | "200": { 89 | "description": "A successful response.", 90 | "schema": { 91 | "properties": {} 92 | } 93 | }, 94 | "403": { 95 | "description": "没权限", 96 | "schema": {} 97 | } 98 | }, 99 | "parameters": [ 100 | { 101 | "name": "id", 102 | "description": "账户id", 103 | "in": "path", 104 | "required": true, 105 | "type": "string", 106 | "format": "int64" 107 | }, 108 | { 109 | "name": "body", 110 | "in": "body", 111 | "required": true, 112 | "schema": { 113 | "$ref": "#/definitions/accountAccountUpdateReq" 114 | } 115 | } 116 | ], 117 | "tags": [ 118 | "AccountService" 119 | ] 120 | } 121 | } 122 | }, 123 | "definitions": { 124 | "AccountBaseLevel": { 125 | "type": "string", 126 | "enum": [ 127 | "LevelDefault", 128 | "LevelOne", 129 | "LevelTwo", 130 | "LevelThree" 131 | ], 132 | "default": "LevelDefault", 133 | "title": "账户级别" 134 | }, 135 | "AccountBaseStatus": { 136 | "type": "string", 137 | "enum": [ 138 | "StatusDefault", 139 | "StatusNormal", 140 | "StatusFrozen" 141 | ], 142 | "default": "StatusDefault", 143 | "description": "- StatusNormal: 正常\n - StatusFrozen: 冻结", 144 | "title": "账户状态" 145 | }, 146 | "accountAccountBase": { 147 | "type": "object", 148 | "properties": { 149 | "id": { 150 | "type": "string", 151 | "format": "int64", 152 | "title": "账户id" 153 | }, 154 | "user_id": { 155 | "type": "string", 156 | "format": "int64", 157 | "title": "用户id" 158 | }, 159 | "account_level": { 160 | "$ref": "#/definitions/AccountBaseLevel", 161 | "title": "账户级别" 162 | }, 163 | "balance": { 164 | "type": "number", 165 | "format": "float", 166 | "title": "余额" 167 | }, 168 | "account_status": { 169 | "$ref": "#/definitions/AccountBaseStatus", 170 | "title": "账户状态" 171 | } 172 | }, 173 | "title": "注入标签: https://github.com/favadi/protoc-go-inject-tag\n 账户基本信息" 174 | }, 175 | "accountAccountUpdateReq": { 176 | "type": "object", 177 | "properties": { 178 | "id": { 179 | "type": "string", 180 | "format": "int64", 181 | "title": "账户id", 182 | "readOnly": true 183 | }, 184 | "balance": { 185 | "type": "number", 186 | "format": "float", 187 | "title": "账户余额" 188 | } 189 | }, 190 | "title": "账户更新请求" 191 | } 192 | }, 193 | "securityDefinitions": { 194 | "ApiKeyAuth": { 195 | "type": "apiKey", 196 | "name": "X-API-Key", 197 | "in": "header" 198 | } 199 | }, 200 | "security": [ 201 | { 202 | "ApiKeyAuth": [] 203 | } 204 | ] 205 | } 206 | -------------------------------------------------------------------------------- /deployments/config/swagger/srv/account/proto/account.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "账户", 5 | "version": "1.0", 6 | "contact": { 7 | "name": "xiaomeng79", 8 | "url": "https://github.com/xiaomeng79", 9 | "email": "mengmeng79@yeah.net" 10 | } 11 | }, 12 | "host": "127.0.0.1:9997", 13 | "schemes": [ 14 | "http" 15 | ], 16 | "consumes": [ 17 | "application/json" 18 | ], 19 | "produces": [ 20 | "application/json" 21 | ], 22 | "paths": { 23 | "/account": { 24 | "post": { 25 | "summary": "账户添加", 26 | "operationId": "AccountAdd", 27 | "responses": { 28 | "200": { 29 | "description": "A successful response.", 30 | "schema": { 31 | "$ref": "#/definitions/accountAccountBase" 32 | } 33 | }, 34 | "403": { 35 | "description": "没权限", 36 | "schema": {} 37 | } 38 | }, 39 | "parameters": [ 40 | { 41 | "name": "body", 42 | "in": "body", 43 | "required": true, 44 | "schema": { 45 | "$ref": "#/definitions/accountAccountBase" 46 | } 47 | } 48 | ], 49 | "tags": [ 50 | "AccountService" 51 | ] 52 | } 53 | }, 54 | "/account/{id}": { 55 | "get": { 56 | "summary": "账户查询一个", 57 | "operationId": "AccountQueryOne", 58 | "responses": { 59 | "200": { 60 | "description": "A successful response.", 61 | "schema": { 62 | "$ref": "#/definitions/accountAccountBase" 63 | } 64 | }, 65 | "403": { 66 | "description": "没权限", 67 | "schema": {} 68 | } 69 | }, 70 | "parameters": [ 71 | { 72 | "name": "id", 73 | "description": "账户id", 74 | "in": "path", 75 | "required": true, 76 | "type": "string", 77 | "format": "int64" 78 | } 79 | ], 80 | "tags": [ 81 | "AccountService" 82 | ] 83 | }, 84 | "put": { 85 | "summary": "账户更新", 86 | "operationId": "AccountUpdate", 87 | "responses": { 88 | "200": { 89 | "description": "A successful response.", 90 | "schema": { 91 | "properties": {} 92 | } 93 | }, 94 | "403": { 95 | "description": "没权限", 96 | "schema": {} 97 | } 98 | }, 99 | "parameters": [ 100 | { 101 | "name": "id", 102 | "description": "账户id", 103 | "in": "path", 104 | "required": true, 105 | "type": "string", 106 | "format": "int64" 107 | }, 108 | { 109 | "name": "body", 110 | "in": "body", 111 | "required": true, 112 | "schema": { 113 | "$ref": "#/definitions/accountAccountUpdateReq" 114 | } 115 | } 116 | ], 117 | "tags": [ 118 | "AccountService" 119 | ] 120 | } 121 | } 122 | }, 123 | "definitions": { 124 | "AccountBaseLevel": { 125 | "type": "string", 126 | "enum": [ 127 | "LevelDefault", 128 | "LevelOne", 129 | "LevelTwo", 130 | "LevelThree" 131 | ], 132 | "default": "LevelDefault", 133 | "title": "账户级别" 134 | }, 135 | "AccountBaseStatus": { 136 | "type": "string", 137 | "enum": [ 138 | "StatusDefault", 139 | "StatusNormal", 140 | "StatusFrozen" 141 | ], 142 | "default": "StatusDefault", 143 | "description": "- StatusNormal: 正常\n - StatusFrozen: 冻结", 144 | "title": "账户状态" 145 | }, 146 | "accountAccountBase": { 147 | "type": "object", 148 | "properties": { 149 | "id": { 150 | "type": "string", 151 | "format": "int64", 152 | "title": "账户id" 153 | }, 154 | "user_id": { 155 | "type": "string", 156 | "format": "int64", 157 | "title": "用户id" 158 | }, 159 | "account_level": { 160 | "$ref": "#/definitions/AccountBaseLevel", 161 | "title": "账户级别" 162 | }, 163 | "balance": { 164 | "type": "number", 165 | "format": "float", 166 | "title": "余额" 167 | }, 168 | "account_status": { 169 | "$ref": "#/definitions/AccountBaseStatus", 170 | "title": "账户状态" 171 | } 172 | }, 173 | "title": "注入标签: https://github.com/favadi/protoc-go-inject-tag\n 账户基本信息" 174 | }, 175 | "accountAccountUpdateReq": { 176 | "type": "object", 177 | "properties": { 178 | "id": { 179 | "type": "string", 180 | "format": "int64", 181 | "title": "账户id", 182 | "readOnly": true 183 | }, 184 | "balance": { 185 | "type": "number", 186 | "format": "float", 187 | "title": "账户余额" 188 | } 189 | }, 190 | "title": "账户更新请求" 191 | } 192 | }, 193 | "securityDefinitions": { 194 | "ApiKeyAuth": { 195 | "type": "apiKey", 196 | "name": "X-API-Key", 197 | "in": "header" 198 | } 199 | }, 200 | "security": [ 201 | { 202 | "ApiKeyAuth": [] 203 | } 204 | ] 205 | } 206 | -------------------------------------------------------------------------------- /srv/user/proto/protoc-gen-swagger/options/annotations.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT.// source: protoc-gen-swagger/options/annotations.proto 2 | 3 | package options // import "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" 4 | 5 | import proto "github.com/golang/protobuf/proto" 6 | import fmt "fmt" 7 | import math "math" 8 | import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" 9 | 10 | // Reference imports to suppress errors if they are not otherwise used. 11 | var _ = proto.Marshal 12 | var _ = fmt.Errorf 13 | var _ = math.Inf 14 | 15 | // This is a compile-time assertion to ensure that this generated file// is compatible with the proto package it is being compiled against.// A compilation error at this line likely means your copy of the// proto package needs to be updated. 16 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 17 | 18 | var E_Openapiv2Swagger = &proto.ExtensionDesc{ 19 | ExtendedType: (*descriptor.FileOptions)(nil), 20 | ExtensionType: (*Swagger)(nil), 21 | Field: 1042, 22 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger", 23 | Tag: "bytes,1042,opt,name=openapiv2_swagger,json=openapiv2Swagger", 24 | Filename: "protoc-gen-swagger/options/annotations.proto", 25 | } 26 | 27 | var E_Openapiv2Operation = &proto.ExtensionDesc{ 28 | ExtendedType: (*descriptor.MethodOptions)(nil), 29 | ExtensionType: (*Operation)(nil), 30 | Field: 1042, 31 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_operation", 32 | Tag: "bytes,1042,opt,name=openapiv2_operation,json=openapiv2Operation", 33 | Filename: "protoc-gen-swagger/options/annotations.proto", 34 | } 35 | 36 | var E_Openapiv2Schema = &proto.ExtensionDesc{ 37 | ExtendedType: (*descriptor.MessageOptions)(nil), 38 | ExtensionType: (*Schema)(nil), 39 | Field: 1042, 40 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_schema", 41 | Tag: "bytes,1042,opt,name=openapiv2_schema,json=openapiv2Schema", 42 | Filename: "protoc-gen-swagger/options/annotations.proto", 43 | } 44 | 45 | var E_Openapiv2Tag = &proto.ExtensionDesc{ 46 | ExtendedType: (*descriptor.ServiceOptions)(nil), 47 | ExtensionType: (*Tag)(nil), 48 | Field: 1042, 49 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_tag", 50 | Tag: "bytes,1042,opt,name=openapiv2_tag,json=openapiv2Tag", 51 | Filename: "protoc-gen-swagger/options/annotations.proto", 52 | } 53 | 54 | var E_Openapiv2Field = &proto.ExtensionDesc{ 55 | ExtendedType: (*descriptor.FieldOptions)(nil), 56 | ExtensionType: (*JSONSchema)(nil), 57 | Field: 1042, 58 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_field", 59 | Tag: "bytes,1042,opt,name=openapiv2_field,json=openapiv2Field", 60 | Filename: "protoc-gen-swagger/options/annotations.proto", 61 | } 62 | 63 | func init() { 64 | proto.RegisterExtension(E_Openapiv2Swagger) 65 | proto.RegisterExtension(E_Openapiv2Operation) 66 | proto.RegisterExtension(E_Openapiv2Schema) 67 | proto.RegisterExtension(E_Openapiv2Tag) 68 | proto.RegisterExtension(E_Openapiv2Field) 69 | } 70 | 71 | func init() { 72 | proto.RegisterFile("protoc-gen-swagger/options/annotations.proto", fileDescriptor_annotations_8378bd63c2853a5a) 73 | } 74 | 75 | var fileDescriptor_annotations_8378bd63c2853a5a = []byte{ 76 | // 346 bytes of a gzipped FileDescriptorProto 77 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xea, 0x40, 78 | 0x14, 0xc5, 0xc3, 0xe6, 0xe5, 0xa5, 0xef, 0xa9, 0x58, 0x37, 0x86, 0xf8, 0x87, 0x9d, 0xc6, 0xc0, 79 | 0x8c, 0x81, 0x5d, 0x77, 0x6a, 0xe2, 0xc2, 0x44, 0x49, 0x0a, 0x2b, 0x37, 0x64, 0x18, 0x2e, 0x97, 80 | 0x49, 0x4a, 0xef, 0x64, 0x66, 0x80, 0x90, 0xb0, 0xf4, 0x13, 0xf8, 0x89, 0x8d, 0xd3, 0xd2, 0x9a, 81 | 0x8a, 0xa6, 0xbb, 0xde, 0xdb, 0x39, 0xe7, 0x77, 0x7a, 0x3a, 0x41, 0x47, 0x1b, 0x72, 0x24, 0xbb, 82 | 0x08, 0x69, 0xd7, 0xae, 0x05, 0x22, 0x18, 0x4e, 0xda, 0x29, 0x4a, 0x2d, 0x17, 0x69, 0x4a, 0x4e, 83 | 0xf8, 0x67, 0xe6, 0x8f, 0x85, 0x57, 0x68, 0xb4, 0x64, 0x28, 0x1c, 0xac, 0xc5, 0x26, 0xdb, 0xc9, 84 | 0x31, 0x42, 0x3a, 0xce, 0xa5, 0x2c, 0x97, 0xb6, 0x6e, 0x7e, 0xb1, 0x25, 0x0d, 0xa9, 0xd0, 0x6a, 85 | 0xd5, 0xcb, 0x0c, 0x5a, 0x6d, 0x24, 0xc2, 0x04, 0xb8, 0x9f, 0x26, 0xcb, 0x19, 0x9f, 0x82, 0x95, 86 | 0x46, 0x69, 0x47, 0x26, 0x3b, 0x11, 0x6d, 0x83, 0xe3, 0x42, 0xb4, 0x43, 0x85, 0x67, 0x2c, 0xd3, 87 | 0xb1, 0x9d, 0x8e, 0x3d, 0xaa, 0x04, 0x06, 0x19, 0xe4, 0xf4, 0xfd, 0x6f, 0xbb, 0x71, 0xfd, 0xaf, 88 | 0x77, 0xcb, 0x6a, 0x26, 0x66, 0xc3, 0x6c, 0x8e, 0x9b, 0x05, 0x29, 0xdf, 0x44, 0x6f, 0x8d, 0xe0, 89 | 0xa4, 0xc4, 0x93, 0x06, 0xe3, 0x3b, 0x09, 0x2f, 0xbe, 0x05, 0x78, 0x06, 0x37, 0xa7, 0x69, 0x25, 90 | 0x42, 0xaf, 0x76, 0x84, 0xc1, 0xce, 0x3a, 0x0e, 0x0b, 0x5e, 0xb1, 0x8b, 0xb6, 0x41, 0xf3, 0x4b, 91 | 0x09, 0x72, 0x0e, 0x0b, 0x11, 0x5e, 0xee, 0x89, 0x60, 0xad, 0xc0, 0x6a, 0x0d, 0xbc, 0x7e, 0x0d, 92 | 0xde, 0x38, 0x3e, 0x2a, 0x5b, 0xf0, 0x8b, 0xc8, 0x06, 0x07, 0x25, 0xdd, 0x09, 0xdc, 0x83, 0x1e, 93 | 0x82, 0x59, 0x29, 0x59, 0x45, 0x77, 0x6a, 0xa3, 0x47, 0x02, 0xe3, 0xff, 0x05, 0x64, 0x24, 0x30, 94 | 0xda, 0x06, 0x65, 0x8e, 0xf1, 0x4c, 0x41, 0x32, 0x0d, 0xcf, 0xf7, 0xfc, 0x75, 0x48, 0xaa, 0x9d, 95 | 0xf7, 0x6b, 0x43, 0x9f, 0x86, 0x83, 0x97, 0xfc, 0x9b, 0x0f, 0x0b, 0x96, 0xb7, 0xbc, 0x7f, 0x78, 96 | 0xbd, 0x43, 0xe5, 0xe6, 0xcb, 0x09, 0x93, 0xb4, 0xe0, 0x9f, 0x86, 0x5d, 0x90, 0x64, 0x37, 0xd6, 97 | 0x41, 0x3e, 0xe6, 0xfe, 0xfc, 0xe7, 0xcb, 0x3e, 0xf9, 0xe3, 0xdf, 0xf5, 0x3f, 0x02, 0x00, 0x00, 98 | 0xff, 0xff, 0x4b, 0xc4, 0x41, 0xfb, 0x68, 0x03, 0x00, 0x00, 99 | } 100 | -------------------------------------------------------------------------------- /srv/account/proto/protoc-gen-swagger/options/annotations.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT.// source: protoc-gen-swagger/options/annotations.proto 2 | 3 | package options // import "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" 4 | 5 | import proto "github.com/golang/protobuf/proto" 6 | import fmt "fmt" 7 | import math "math" 8 | import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" 9 | 10 | // Reference imports to suppress errors if they are not otherwise used. 11 | var _ = proto.Marshal 12 | var _ = fmt.Errorf 13 | var _ = math.Inf 14 | 15 | // This is a compile-time assertion to ensure that this generated file// is compatible with the proto package it is being compiled against.// A compilation error at this line likely means your copy of the// proto package needs to be updated. 16 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 17 | 18 | var E_Openapiv2Swagger = &proto.ExtensionDesc{ 19 | ExtendedType: (*descriptor.FileOptions)(nil), 20 | ExtensionType: (*Swagger)(nil), 21 | Field: 1042, 22 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger", 23 | Tag: "bytes,1042,opt,name=openapiv2_swagger,json=openapiv2Swagger", 24 | Filename: "protoc-gen-swagger/options/annotations.proto", 25 | } 26 | 27 | var E_Openapiv2Operation = &proto.ExtensionDesc{ 28 | ExtendedType: (*descriptor.MethodOptions)(nil), 29 | ExtensionType: (*Operation)(nil), 30 | Field: 1042, 31 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_operation", 32 | Tag: "bytes,1042,opt,name=openapiv2_operation,json=openapiv2Operation", 33 | Filename: "protoc-gen-swagger/options/annotations.proto", 34 | } 35 | 36 | var E_Openapiv2Schema = &proto.ExtensionDesc{ 37 | ExtendedType: (*descriptor.MessageOptions)(nil), 38 | ExtensionType: (*Schema)(nil), 39 | Field: 1042, 40 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_schema", 41 | Tag: "bytes,1042,opt,name=openapiv2_schema,json=openapiv2Schema", 42 | Filename: "protoc-gen-swagger/options/annotations.proto", 43 | } 44 | 45 | var E_Openapiv2Tag = &proto.ExtensionDesc{ 46 | ExtendedType: (*descriptor.ServiceOptions)(nil), 47 | ExtensionType: (*Tag)(nil), 48 | Field: 1042, 49 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_tag", 50 | Tag: "bytes,1042,opt,name=openapiv2_tag,json=openapiv2Tag", 51 | Filename: "protoc-gen-swagger/options/annotations.proto", 52 | } 53 | 54 | var E_Openapiv2Field = &proto.ExtensionDesc{ 55 | ExtendedType: (*descriptor.FieldOptions)(nil), 56 | ExtensionType: (*JSONSchema)(nil), 57 | Field: 1042, 58 | Name: "grpc.gateway.protoc_gen_swagger.options.openapiv2_field", 59 | Tag: "bytes,1042,opt,name=openapiv2_field,json=openapiv2Field", 60 | Filename: "protoc-gen-swagger/options/annotations.proto", 61 | } 62 | 63 | func init() { 64 | proto.RegisterExtension(E_Openapiv2Swagger) 65 | proto.RegisterExtension(E_Openapiv2Operation) 66 | proto.RegisterExtension(E_Openapiv2Schema) 67 | proto.RegisterExtension(E_Openapiv2Tag) 68 | proto.RegisterExtension(E_Openapiv2Field) 69 | } 70 | 71 | func init() { 72 | proto.RegisterFile("protoc-gen-swagger/options/annotations.proto", fileDescriptor_annotations_8378bd63c2853a5a) 73 | } 74 | 75 | var fileDescriptor_annotations_8378bd63c2853a5a = []byte{ 76 | // 346 bytes of a gzipped FileDescriptorProto 77 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xea, 0x40, 78 | 0x14, 0xc5, 0xc3, 0xe6, 0xe5, 0xa5, 0xef, 0xa9, 0x58, 0x37, 0x86, 0xf8, 0x87, 0x9d, 0xc6, 0xc0, 79 | 0x8c, 0x81, 0x5d, 0x77, 0x6a, 0xe2, 0xc2, 0x44, 0x49, 0x0a, 0x2b, 0x37, 0x64, 0x18, 0x2e, 0x97, 80 | 0x49, 0x4a, 0xef, 0x64, 0x66, 0x80, 0x90, 0xb0, 0xf4, 0x13, 0xf8, 0x89, 0x8d, 0xd3, 0xd2, 0x9a, 81 | 0x8a, 0xa6, 0xbb, 0xde, 0xdb, 0x39, 0xe7, 0x77, 0x7a, 0x3a, 0x41, 0x47, 0x1b, 0x72, 0x24, 0xbb, 82 | 0x08, 0x69, 0xd7, 0xae, 0x05, 0x22, 0x18, 0x4e, 0xda, 0x29, 0x4a, 0x2d, 0x17, 0x69, 0x4a, 0x4e, 83 | 0xf8, 0x67, 0xe6, 0x8f, 0x85, 0x57, 0x68, 0xb4, 0x64, 0x28, 0x1c, 0xac, 0xc5, 0x26, 0xdb, 0xc9, 84 | 0x31, 0x42, 0x3a, 0xce, 0xa5, 0x2c, 0x97, 0xb6, 0x6e, 0x7e, 0xb1, 0x25, 0x0d, 0xa9, 0xd0, 0x6a, 85 | 0xd5, 0xcb, 0x0c, 0x5a, 0x6d, 0x24, 0xc2, 0x04, 0xb8, 0x9f, 0x26, 0xcb, 0x19, 0x9f, 0x82, 0x95, 86 | 0x46, 0x69, 0x47, 0x26, 0x3b, 0x11, 0x6d, 0x83, 0xe3, 0x42, 0xb4, 0x43, 0x85, 0x67, 0x2c, 0xd3, 87 | 0xb1, 0x9d, 0x8e, 0x3d, 0xaa, 0x04, 0x06, 0x19, 0xe4, 0xf4, 0xfd, 0x6f, 0xbb, 0x71, 0xfd, 0xaf, 88 | 0x77, 0xcb, 0x6a, 0x26, 0x66, 0xc3, 0x6c, 0x8e, 0x9b, 0x05, 0x29, 0xdf, 0x44, 0x6f, 0x8d, 0xe0, 89 | 0xa4, 0xc4, 0x93, 0x06, 0xe3, 0x3b, 0x09, 0x2f, 0xbe, 0x05, 0x78, 0x06, 0x37, 0xa7, 0x69, 0x25, 90 | 0x42, 0xaf, 0x76, 0x84, 0xc1, 0xce, 0x3a, 0x0e, 0x0b, 0x5e, 0xb1, 0x8b, 0xb6, 0x41, 0xf3, 0x4b, 91 | 0x09, 0x72, 0x0e, 0x0b, 0x11, 0x5e, 0xee, 0x89, 0x60, 0xad, 0xc0, 0x6a, 0x0d, 0xbc, 0x7e, 0x0d, 92 | 0xde, 0x38, 0x3e, 0x2a, 0x5b, 0xf0, 0x8b, 0xc8, 0x06, 0x07, 0x25, 0xdd, 0x09, 0xdc, 0x83, 0x1e, 93 | 0x82, 0x59, 0x29, 0x59, 0x45, 0x77, 0x6a, 0xa3, 0x47, 0x02, 0xe3, 0xff, 0x05, 0x64, 0x24, 0x30, 94 | 0xda, 0x06, 0x65, 0x8e, 0xf1, 0x4c, 0x41, 0x32, 0x0d, 0xcf, 0xf7, 0xfc, 0x75, 0x48, 0xaa, 0x9d, 95 | 0xf7, 0x6b, 0x43, 0x9f, 0x86, 0x83, 0x97, 0xfc, 0x9b, 0x0f, 0x0b, 0x96, 0xb7, 0xbc, 0x7f, 0x78, 96 | 0xbd, 0x43, 0xe5, 0xe6, 0xcb, 0x09, 0x93, 0xb4, 0xe0, 0x9f, 0x86, 0x5d, 0x90, 0x64, 0x37, 0xd6, 97 | 0x41, 0x3e, 0xe6, 0xfe, 0xfc, 0xe7, 0xcb, 0x3e, 0xf9, 0xe3, 0xdf, 0xf5, 0x3f, 0x02, 0x00, 0x00, 98 | 0xff, 0xff, 0x4b, 0xc4, 0x41, 0xfb, 0x68, 0x03, 0x00, 0x00, 99 | } 100 | --------------------------------------------------------------------------------