Commit f7a44891 authored by kzkzzzz's avatar kzkzzzz

feat: 初始化go-micro

parents
.idea
.vscode
tmp
bin
temp
*.log
*.yaml
.DS_Store
\ No newline at end of file
project=demo
API_PROTO_FILES=$(shell find api/$(project) -name *.proto)
# go-micro仓库https://github.com/asim/go-micro
# 安装命令行工具
.PHONY: init
init:
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go get -u google.golang.org/protobuf/proto
go install github.com/golang/protobuf/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go install github.com/asim/go-micro/cmd/protoc-gen-micro/v4@latest
go install github.com/favadi/protoc-go-inject-tag@latest
go install github.com/kzkzzzz/dbtogo@main
go install github.com/go-micro/cli/cmd/go-micro@latest
go install github.com/cosmtrek/air@latest
.PHONY: gen
gen:
@bash script/gen.sh $(project)
@make api project=$(project)
# 根据 api/服务/service里面的proto文件 生成, mac和linux分别移除omitempty标签
# protoc-go-inject-tag用于自定义json等tag
.PHONY: api
api:
protoc --proto_path=./api/$(project) \
--go_out=:./api/$(project) --go_opt=paths=source_relative \
--micro_out=:./api/$(project) --micro_opt=paths=source_relative \
$(API_PROTO_FILES)
protoc-go-inject-tag -input=api/$(project)/*.pb.go
@grep -rl 'omitempty' api/$(project)/*.pb.go | xargs -I {} sed -i "s#,omitempty##g" {} || true
@grep -rl 'omitempty' api/$(project)/*.pb.go | xargs -I {} sed -i '' "s#,omitempty##g" {} || true
.PHONY: tidy
tidy:
@go mod download
@go mod tidy
.PHONY: run
run:
@cd service/$(project) && go-micro run
.PHONY: build
build:
@cd service/$(project) && go build -v -o ./bin/server .
### 基于go-micro构建微服务, 采用单体仓库monorepo方案
#### https://github.com/asim/go-micro
### 相关目录说明
```text
api 存放对应服务的proto文件
third_party 存放第三方的proto文件
service 一个目录一个服务
common 通用的工具类
script 相关shell脚本
tool 相关工具服务
tool/gateway 开发调试网关, 例如: go run gateway.go -c 192.168.233.1:8500
```
#### 初始化, 下载依赖工具
```shell
make init
```
#### 新建项目, 主要是复制demo项目模板并进行替换
```shell
# 创建user服务
make gen project=user
```
#### 开发环境运行服务
```shell
# 运行demo服务, 修改代码自动热重载
make run project=demo
```
#### 编译
```shell
make build project=demo
```
This diff is collapsed.
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: demo.proto
package demo
import (
fmt "fmt"
proto "google.golang.org/protobuf/proto"
_ "google.golang.org/protobuf/types/known/timestamppb"
math "math"
)
import (
context "context"
api "go-micro.dev/v4/api"
client "go-micro.dev/v4/client"
server "go-micro.dev/v4/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Demo service
func NewDemoEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Demo service
type DemoService interface {
GetDemo(ctx context.Context, in *DemoReq, opts ...client.CallOption) (*DemoResp, error)
ListDemo(ctx context.Context, in *ListDemoReq, opts ...client.CallOption) (*ListDemoResp, error)
}
type demoService struct {
c client.Client
name string
}
func NewDemoService(name string, c client.Client) DemoService {
return &demoService{
c: c,
name: name,
}
}
func (c *demoService) GetDemo(ctx context.Context, in *DemoReq, opts ...client.CallOption) (*DemoResp, error) {
req := c.c.NewRequest(c.name, "Demo.GetDemo", in)
out := new(DemoResp)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *demoService) ListDemo(ctx context.Context, in *ListDemoReq, opts ...client.CallOption) (*ListDemoResp, error) {
req := c.c.NewRequest(c.name, "Demo.ListDemo", in)
out := new(ListDemoResp)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Demo service
type DemoHandler interface {
GetDemo(context.Context, *DemoReq, *DemoResp) error
ListDemo(context.Context, *ListDemoReq, *ListDemoResp) error
}
func RegisterDemoHandler(s server.Server, hdlr DemoHandler, opts ...server.HandlerOption) error {
type demo interface {
GetDemo(ctx context.Context, in *DemoReq, out *DemoResp) error
ListDemo(ctx context.Context, in *ListDemoReq, out *ListDemoResp) error
}
type Demo struct {
demo
}
h := &demoHandler{hdlr}
return s.Handle(s.NewHandler(&Demo{h}, opts...))
}
type demoHandler struct {
DemoHandler
}
func (h *demoHandler) GetDemo(ctx context.Context, in *DemoReq, out *DemoResp) error {
return h.DemoHandler.GetDemo(ctx, in, out)
}
func (h *demoHandler) ListDemo(ctx context.Context, in *ListDemoReq, out *ListDemoResp) error {
return h.DemoHandler.ListDemo(ctx, in, out)
}
syntax = "proto3";
import "google/protobuf/timestamp.proto";
option go_package = "./;demo";
message DemoReq {
string id = 1; // @gotags: validate:"required"
}
message ListDemoReq {
string id = 1; // @gotags: validate:"required"
int32 limit = 2;
}
message DemoResp {
string id = 1;
string username = 2;
string email = 3;
string avatar = 4;
int32 status = 5;
google.protobuf.Timestamp created_at = 6;
google.protobuf.Timestamp updated_at = 7;
}
message ListDemoResp {
repeated DemoResp list = 1;
int32 limit = 2;
}
service Demo {
rpc GetDemo(DemoReq) returns (DemoResp){};
rpc ListDemo(ListDemoReq) returns (ListDemoResp){};
}
package conf
import (
"fmt"
"github.com/spf13/viper"
)
func LoadFromYaml(configFile string, conf interface{}) {
v := viper.New()
v.SetConfigFile(configFile)
err := v.ReadInConfig()
if err != nil {
panic(fmt.Errorf("读取配置文件失败: %s", err))
}
err = v.Unmarshal(&conf)
if err != nil {
panic(fmt.Errorf("解析配置文件失败: %s", err))
}
return
}
package errorx
import (
"fmt"
"github.com/go-playground/validator/v10"
"gokratos-base/common/validate"
)
const (
DefaultError = 500
ParamsError = 400
)
type ResponseError struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
var _ error = &ResponseError{}
func (c *ResponseError) Error() string {
return fmt.Sprintf("[%d]%s", c.Code, c.Msg)
}
func NewError(message string, code ...int) error {
var c = DefaultError
if len(code) > 0 {
c = code[0]
}
e := &ResponseError{
Code: c,
Msg: message,
}
return e
}
func ParseError(err error) (errorCode int, msg string) {
switch v := err.(type) {
case *ResponseError:
errorCode = v.Code
msg = v.Msg
case validator.ValidationErrors:
errorCode = ParamsError
if len(v) > 0 {
msg = v[0].Translate(validate.GrtTrans())
} else {
msg = v.Error()
}
case validator.FieldError:
errorCode = ParamsError
msg = v.Translate(validate.GrtTrans())
default:
errorCode = DefaultError
msg = err.Error()
}
return
}
package hashutil
import (
"crypto/md5"
"encoding/hex"
"github.com/spf13/cast"
)
func Md5(s interface{}) string {
m := md5.New()
m.Write([]byte(cast.ToString(s)))
return hex.EncodeToString(m.Sum(nil))
}
package logz
import "go-micro.dev/v4/logger"
// 实现go-micro的logger
var _ logger.Logger = &zapStd{}
func (z *zapStd) Init(options ...logger.Option) error {
return nil
}
func (z *zapStd) Options() logger.Options {
return logger.Options{}
}
func (z *zapStd) Fields(fields map[string]interface{}) logger.Logger {
//args := make([]interface{}, 0, len(fields))
//for k, v := range fields {
// args = append(args, k)
// args = append(args, v)
//}
//z.log.With(args...)
return z
}
func (z *zapStd) Log(level logger.Level, v ...interface{}) {
switch level {
case logger.DebugLevel:
z.log.Debug(v...)
case logger.InfoLevel:
z.log.Info(v...)
case logger.WarnLevel:
z.log.Warn(v...)
case logger.ErrorLevel:
z.log.Error(v...)
case logger.FatalLevel:
z.log.Fatal(v...)
default:
z.log.Info(v...)
}
}
func (z *zapStd) Logf(level logger.Level, format string, v ...interface{}) {
switch level {
case logger.DebugLevel:
z.log.Debugf(format, v...)
case logger.InfoLevel:
z.log.Infof(format, v...)
case logger.WarnLevel:
z.log.Warnf(format, v...)
case logger.ErrorLevel:
z.log.Errorf(format, v...)
case logger.FatalLevel:
z.log.Fatalf(format, v...)
default:
z.log.Infof(format, v...)
}
}
func (z zapStd) String() string {
return "zap logger"
}
package logz
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var (
// kratos映射zap的level
levelMap = map[string]zapcore.Level{
"DEBUG": zapcore.DebugLevel,
"INFO": zapcore.InfoLevel,
"WARN": zapcore.WarnLevel,
"ERROR": zapcore.ErrorLevel,
"FATAL": zapcore.FatalLevel,
}
// 默认配置
defaultConfig = &Config{
Color: true,
Level: "DEBUG",
}
zapStdLog *zapStd
)
type (
zapStd struct {
log *zap.SugaredLogger
}
Config struct {
Color bool
Level string
}
)
func NewLogger(conf *Config) *zapStd {
if conf == nil {
conf = defaultConfig
}
var level zapcore.Level
if v, ok := levelMap[conf.Level]; ok {
level = v
} else {
level = zapcore.InfoLevel
}
cfg := zap.NewProductionEncoderConfig()
if conf.Color {
// 颜色
cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
} else {
cfg.EncodeLevel = zapcore.CapitalLevelEncoder
}
//cfg.ConsoleSeparator = strings.Repeat(" ", 2)
cfg.ConsoleSeparator = " ---- "
// 指定日志时间格式
//cfg.EncodeTime = zapcore.ISO8601TimeEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
//cfg.EncodeCaller = zapcore.FullCallerEncoder
// 使用控制台输出
encoder := zapcore.NewConsoleEncoder(cfg)
core := zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), level)
//core := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(ws...), level)
l := zap.New(zapcore.NewTee(core), zap.AddCaller(), zap.AddCallerSkip(1))
zapStdLog = &zapStd{
log: l.Sugar(),
}
return zapStdLog
}
func With(args ...interface{}) *zap.SugaredLogger {
return zapStdLog.log.With(args...)
}
func Debug(args ...interface{}) {
zapStdLog.log.Debug(args...)
}
func Info(args ...interface{}) {
zapStdLog.log.Info(args...)
}
func Warn(args ...interface{}) {
zapStdLog.log.Warn(args...)
}
func Error(args ...interface{}) {
zapStdLog.log.Error(args...)
}
func Fatal(args ...interface{}) {
zapStdLog.log.Fatal(args...)
}
func Debugf(format string, args ...interface{}) {
zapStdLog.log.Debugf(format, args...)
}
func Infof(format string, args ...interface{}) {
zapStdLog.log.Infof(format, args...)
}
func Warnf(format string, args ...interface{}) {
zapStdLog.log.Warnf(format, args...)
}
func Errorf(format string, args ...interface{}) {
zapStdLog.log.Errorf(format, args...)
}
func Fatalf(format string, args ...interface{}) {
zapStdLog.log.Fatalf(format, args...)
}
func Println(format string) {
zapStdLog.log.Info(format)
}
func Printf(format string, args ...interface{}) {
zapStdLog.log.Infof(format, args...)
}
func Flush() {
zapStdLog.log.Sync()
}
package mysql
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
gormLogger "gorm.io/gorm/logger"
"time"
)
type Config struct {
Dsn string
MaxConn int
MaxIdleConn int
MaxLifetime int
Debug bool
}
func NewDB(conf *Config) (db *gorm.DB) {
var err error
var level = gormLogger.Warn
if conf.Debug {
level = gormLogger.Info
}
db, err = gorm.Open(mysql.Open(conf.Dsn), &gorm.Config{Logger: gormLogger.Default.LogMode(level)})
if err != nil {
panic(fmt.Sprintf("连接mysql失败(-1): %s", err))
}
s, _ := db.DB()
err = s.Ping()
if err != nil {
panic(fmt.Sprintf("连接mysql失败(-2): %s", err))
}
s.SetMaxOpenConns(conf.MaxConn)
s.SetMaxIdleConns(conf.MaxIdleConn)
s.SetConnMaxLifetime(time.Duration(conf.MaxLifetime) * time.Second)
return
}
func newDBLogger(writer gormLogger.Writer, level gormLogger.LogLevel) gormLogger.Interface {
return gormLogger.New(writer, gormLogger.Config{
SlowThreshold: time.Second * 2, // 慢查询阈值
Colorful: true,
IgnoreRecordNotFoundError: true,
LogLevel: level,
})
}
package redis
import (
"context"
"encoding/json"
"fmt"
"github.com/go-redis/redis/v8"
"github.com/pkg/errors"
"time"
)
type (
WrapClient struct {
*redis.Client
ctx context.Context
}
Config struct {
Addr string
Password string
DB int
}
)
func NewRedis(conf *Config) (c *WrapClient) {
client := redis.NewClient(&redis.Options{
Addr: conf.Addr,
Password: conf.Password,
DB: conf.DB,
MaxConnAge: time.Minute * 30, // 连接池连接有效时间
MinIdleConns: 4,
})
ctx := context.Background()
c = &WrapClient{}
c.ctx = ctx
c.Client = client
ping := c.Ping(ctx)
if ping.Err() != nil {
panic(errors.Errorf("连接redis失败:%s", ping.Err()))
}
return
}
// GetString 字符串
func (r *WrapClient) GetString(key string) (string, error) {
return r.Client.Get(r.ctx, key).Result()
}
// SetSimple 通用set
func (r *WrapClient) SetSimple(key string, value interface{}, t ...time.Duration) (string, error) {
var t2 time.Duration
if len(t) > 0 {
t2 = t[0]
}
return r.Client.Set(r.ctx, key, value, t2).Result()
}
//GetJson json序列化
func (r *WrapClient) GetJson(key string) (interface{}, error) {
res := r.Client.Get(r.ctx, key)
if res.Err() != nil {
return nil, res.Err()
}
b, err := res.Bytes()
if err != nil {
return nil, errors.Errorf("get key:%s 反序列化json失败(-1)", key)
}
var result interface{}
err = json.Unmarshal(b, &result)
if err != nil {
return nil, errors.Errorf("get key:%s 反序列化json失败(-2)", key)
}
return result, nil
}
//SetJson json序列化set
func (r *WrapClient) SetJson(key string, value interface{}, t ...time.Duration) (string, error) {
var t2 time.Duration
if len(t) > 0 {
t2 = t[0]
}
v, err := json.Marshal(value)
if err != nil {
return "", fmt.Errorf("set key:%s 序列化json失败", key)
}
return r.Client.Set(r.ctx, key, v, t2).Result()
}
package resutil
import (
"encoding/json"
"github.com/go-kratos/kratos/v2/log"
"net/http"
)
func HttpResponseEncoder(w http.ResponseWriter, r *http.Request, v interface{}) error {
if v == nil {
_, err := w.Write(nil)
return err
}
data, err := json.Marshal(MakeHttpSuccess(v))
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
_, err = w.Write(data)
if err != nil {
return err
}
return nil
}
func HttpErrorEncoder(w http.ResponseWriter, r *http.Request, err error) {
data, err := json.Marshal(MakeHttpError(err))
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
log.Errorf("HttpErrorEncoder json编码失败: %s", err)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write(data)
}
package resutil
import (
"gokratos-base/common/errorx"
)
type HttpResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data,omitempty"`
}
func MakeHttpSuccess(data interface{}) (r *HttpResponse) {
r = &HttpResponse{
Code: 1,
Msg: "ok",
Data: data,
}
return
}
func MakeHttpError(err error) (r *HttpResponse) {
r = &HttpResponse{}
r.Code, r.Msg = errorx.ParseError(err)
return
}
package validate
import (
"github.com/go-playground/locales/en"
"github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)
var (
vt *validator.Validate
tr ut.Translator
)
func newTranslate(v *validator.Validate) ut.Translator {
zhT := zh.New()
enT := en.New()
uni := ut.New(enT, zhT)
trans, _ := uni.GetTranslator("zh")
_ = zhTranslations.RegisterDefaultTranslations(v, trans)
return trans
}
func init() {
vt = validator.New()
tr = newTranslate(vt)
}
func GrtTrans() ut.Translator {
return tr
}
func Struct(data interface{}) error {
return vt.Struct(data)
}
module gomicro-base
go 1.18
require (
github.com/asim/go-micro/plugins/registry/consul/v4 v4.0.0-20220530075002-cf51ddeb26c8
github.com/asim/go-micro/plugins/server/grpc/v4 v4.0.0-20220511085541-13b76331ec6f
github.com/go-kratos/kratos/v2 v2.3.1
github.com/go-playground/locales v0.14.0
github.com/go-playground/universal-translator v0.18.0
github.com/go-playground/validator/v10 v10.11.0
github.com/go-redis/redis/v8 v8.11.5
github.com/pkg/errors v0.9.1
github.com/spf13/cast v1.5.0
github.com/spf13/viper v1.12.0
go-micro.dev/v4 v4.7.0
go.uber.org/zap v1.21.0
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
google.golang.org/protobuf v1.28.0
gorm.io/driver/mysql v1.3.3
gorm.io/gorm v1.23.5
)
require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/armon/go-metrics v0.3.10 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-git/v5 v5.4.2 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/consul/api v1.12.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/serf v0.9.7 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/miekg/dns v1.1.49 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.2 // indirect
github.com/subosito/gotenv v1.3.0 // indirect
github.com/urfave/cli/v2 v2.8.1 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac // indirect
google.golang.org/grpc v1.47.0 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
This diff is collapsed.
#!/bin/bash
os=$(uname -s)
project=$1
parent_dir=$(dirname $(cd $(dirname "$0") && pwd))
# 检查项目名, 目录是否存在
if [[ -z $project ]]; then
echo '未指定项目名'
exit 0
fi
api_dir="$parent_dir/api/$project"
service_dir="$parent_dir/service/$project"
if [[ -e $api_dir || -e $service_dir ]]; then
echo "${project} 项目已存在"
exit 0
fi
echo $api_dir
echo $service_dir
mkdir -p $api_dir $service_dir
cp -r $parent_dir/api/demo/demo.proto $api_dir/$project.proto && cp -r $parent_dir/service/demo/ $service_dir/
rm -rf $service_dir/tmp $service_dir/bin
# 项目名首字母大写
up_case_project="$(tr '[:lower:]' '[:upper:]' <<< ${project:0:1})${project:1}"
echo $service_dir
# 替换相关内容
case $os in
# mac系统
'Darwin')
# 替换service和api的内容
grep -rl 'demo' $service_dir | xargs -I {} sed -i '' "s#demo#${project}#g" {}
grep -rl 'Demo' $service_dir | xargs -I {} sed -i '' "s#Demo#${up_case_project}#g" {}
grep -rl 'demo' $api_dir | xargs -I {} sed -i '' "s#demo#${project}#g" {}
grep -rl 'Demo' $api_dir | xargs -I {} sed -i '' "s#Demo#${up_case_project}#g" {}
echo 'macos sed'
;;
*)
# linux系统, win用wsl2或git bash执行, 主要跟mac系统差个 -i ''
grep -rl 'demo' $service_dir | xargs -I {} sed -i "s#demo#${project}#g" {}
grep -rl 'Demo' $service_dir | xargs -I {} sed -i "s#Demo#${up_case_project}#g" {}
grep -rl 'demo' $api_dir | xargs -I {} sed -i "s#demo#${project}#g" {}
grep -rl 'Demo' $api_dir | xargs -I {} sed -i "s#Demo#${up_case_project}#g" {}
echo 'linux sed'
;;
esac
\ No newline at end of file
# air热更新https://github.com/cosmtrek/air 或者 使用go-micro
root = "."
testdata_dir = "testdata"
tmp_dir = "bin"
[build]
bin = "./bin/server"
cmd = "go build -o ./bin/server ."
delay = 100
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = 100
log = "build-errors.log"
send_interrupt = true
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
time = true
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
tmp
temp
bin
*.log
\ No newline at end of file
package conf
import (
"flag"
"gomicro-base/common/conf"
"gomicro-base/common/logz"
"gomicro-base/common/mysql"
)
func init() {
flag.StringVar(&confPath, "conf", "config/config.yaml", "配置文件路径")
}
var (
confPath string
Conf = &Config{}
)
type Config struct {
Server *Server
Mysql *mysql.Config
Log *logz.Config
}
type Server struct {
Name string
ConsulServer []string
EtcdServer []string
Grpc struct {
Addr string
}
Http struct {
Addr string
}
}
func LoadConfig() {
conf.LoadFromYaml(confPath, &Conf)
}
package dao
import (
"gomicro-base/common/mysql"
"gomicro-base/service/demo/internal/conf"
"gomicro-base/service/demo/internal/model"
"gorm.io/gorm"
)
// Dao 查询数据库
type Dao struct {
DB *gorm.DB
}
func New(c *conf.Config) (d *Dao) {
d = &Dao{
DB: mysql.NewDB(c.Mysql),
}
return
}
// GetUserById 根据id查询用户
func (d *Dao) GetUserById(id int, column []string) (res *model.User, err error) {
res = &model.User{}
err = d.DB.Model(&model.User{}).
Select(column).
Where("id = ?", id).Take(res).Error
return
}
// ListUserGtId 查询大于该id的用户列表
func (d *Dao) ListUserGtId(id int, limit int) (res []*model.User, err error) {
res = make([]*model.User, 0)
err = d.DB.Model(&model.User{}).
Where("id > ?", id).
Limit(limit).Order("id desc").
Find(&res).Error
return
}
package model
import (
"time"
)
type User struct {
Id int `json:"id"` // Id
Username string `json:"username"` // Username
IsEmailVerified int `json:"is_email_verified"` // IsEmailVerified
Email string `json:"email"` // Email
Password string `json:"password"` // Password
PasswordHash string `json:"password_hash"` // PasswordHash
Avatar string `json:"avatar"` // Avatar
Role string `json:"role"` // Role
Status int `json:"status"` // Status
LastSpace int `json:"last_space"` // LastSpace
CreatedAt time.Time `json:"created_at"` // CreatedAt
UpdatedAt time.Time `json:"updated_at"` // UpdatedAt
}
func (u *User) TableName() string {
return "users"
}
package server
import (
"fmt"
"github.com/asim/go-micro/plugins/registry/consul/v4"
"github.com/asim/go-micro/plugins/server/grpc/v4"
"go-micro.dev/v4"
"go-micro.dev/v4/registry"
"go-micro.dev/v4/server"
"gomicro-base/api/demo"
"gomicro-base/service/demo/internal/conf"
"gomicro-base/service/demo/internal/service"
"time"
)
func NewMicroServer(conf *conf.Config, svc *service.DemoService) micro.Service {
reg := consul.NewRegistry(registry.Addrs(conf.Server.ConsulServer...))
//reg := etcd.NewRegistry(registry.Addrs(conf.Server.EtcdServer...))
opt := []micro.Option{
micro.Server(grpc.NewServer()),
micro.Name(conf.Server.Name),
micro.Address(conf.Server.Grpc.Addr),
micro.Registry(reg),
micro.RegisterTTL(time.Second * 30),
micro.RegisterInterval(time.Second * 10),
}
s := micro.NewService(opt...)
s.Server().Init(server.Wait(nil))
//reflection.Register()
s.Init()
err := demo.RegisterDemoHandler(s.Server(), &handler{svc: svc})
if err != nil {
fmt.Println(err)
}
return s
}
package server
import (
"context"
"github.com/spf13/cast"
"gomicro-base/api/demo"
"gomicro-base/service/demo/internal/model"
"gomicro-base/service/demo/internal/service"
"google.golang.org/protobuf/types/known/timestamppb"
)
// 检查接口实现
var _ demo.DemoHandler = &handler{}
// handler类似控制器
type handler struct {
svc *service.DemoService
}
func (h *handler) GetDemo(ctx context.Context, req *demo.DemoReq, resp *demo.DemoResp) error {
getDemo, err := h.svc.GetDemo(ctx, req)
if err != nil {
return err
}
*resp = h.convertUserModel(getDemo)
return nil
}
func (h *handler) ListDemo(ctx context.Context, req *demo.ListDemoReq, resp *demo.ListDemoResp) error {
if req.Limit <= 0 {
req.Limit = 15
}
listDemo, err := h.svc.ListDemo(ctx, req)
if err != nil {
return err
}
list := make([]*demo.DemoResp, 0)
for _, v := range listDemo {
c := h.convertUserModel(v)
list = append(list, &c)
}
resp.List = list
resp.Limit = req.Limit
return nil
}
func (h *handler) convertUserModel(users *model.User) (resp demo.DemoResp) {
return demo.DemoResp{
Id: cast.ToString(users.Id),
Username: users.Username,
Email: users.Email,
Avatar: users.Avatar,
Status: int32(users.Status),
CreatedAt: timestamppb.New(users.CreatedAt),
UpdatedAt: timestamppb.New(users.UpdatedAt),
}
}
package service
import (
"context"
"github.com/spf13/cast"
"gomicro-base/api/demo"
"gomicro-base/service/demo/internal/dao"
"gomicro-base/service/demo/internal/model"
)
// DemoService 服务层处理逻辑
type DemoService struct {
dao *dao.Dao
}
func New(d *dao.Dao) *DemoService {
return &DemoService{
dao: d,
}
}
func (s *DemoService) GetDemo(ctx context.Context, req *demo.DemoReq) (*model.User, error) {
user, err := s.dao.GetUserById(cast.ToInt(req.Id), []string{"*"})
if err != nil {
return nil, err
}
return user, err
}
func (s *DemoService) ListDemo(ctx context.Context, req *demo.ListDemoReq) ([]*model.User, error) {
list, err := s.dao.ListUserGtId(cast.ToInt(req.Id), int(req.Limit))
if err != nil {
return nil, err
}
return list, nil
}
package main
import (
"flag"
"fmt"
"go-micro.dev/v4/logger"
"golang.org/x/sync/errgroup"
"gomicro-base/common/logz"
"gomicro-base/service/demo/internal/conf"
"gomicro-base/service/demo/internal/dao"
"gomicro-base/service/demo/internal/server"
"gomicro-base/service/demo/internal/service"
"os"
"os/signal"
"syscall"
)
func main() {
flag.Parse()
conf.LoadConfig()
if conf.Conf.Server.Name == "" {
panic("服务名不能为空")
}
// github.com/go-micro/plugins/v4/logger/zap
logger.DefaultLogger = logz.NewLogger(conf.Conf.Log)
defer logz.Flush()
svc := service.New(dao.New(conf.Conf))
microServer := server.NewMicroServer(conf.Conf, svc)
eg := errgroup.Group{}
eg.Go(func() error {
return microServer.Run()
})
eg.Go(func() error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
v := <-ch
fmt.Println()
logz.Warnf("捕获退出信号: %v", v)
return nil
})
if err := eg.Wait(); err != nil {
logz.Error(err)
}
}
// 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.
syntax = "proto3";
package google.api;
import "google/api/http.proto";
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.MethodOptions {
// See `HttpRule`.
HttpRule http = 72295728;
}
// Copyright 2019 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
//
// https://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.
syntax = "proto3";
package google.api;
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "ClientProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.ServiceOptions {
// The hostname for this service.
// This should be specified with no prefix or protocol.
//
// Example:
//
// service Foo {
// option (google.api.default_host) = "foo.googleapi.com";
// ...
// }
string default_host = 1049;
// OAuth scopes needed for the client.
//
// Example:
//
// service Foo {
// option (google.api.oauth_scopes) = \
// "https://www.googleapis.com/auth/cloud-platform";
// ...
// }
//
// If there is more than one scope, use a comma-separated string:
//
// Example:
//
// service Foo {
// option (google.api.oauth_scopes) = \
// "https://www.googleapis.com/auth/cloud-platform,"
// "https://www.googleapis.com/auth/monitoring";
// ...
// }
string oauth_scopes = 1050;
}
extend google.protobuf.MethodOptions {
// A definition of a client library method signature.
//
// In client libraries, each proto RPC corresponds to one or more methods
// which the end user is able to call, and calls the underlying RPC.
// Normally, this method receives a single argument (a struct or instance
// corresponding to the RPC request object). Defining this field will
// add one or more overloads providing flattened or simpler method signatures
// in some languages.
//
// The fields on the method signature are provided as a comma-separated
// string.
//
// For example, the proto RPC and annotation:
//
// rpc CreateSubscription(CreateSubscriptionRequest)
// returns (Subscription) {
// option (google.api.method_signature) = "name,topic";
// }
//
// Would add the following Java overload (in addition to the method accepting
// the request object):
//
// public final Subscription createSubscription(String name, String topic)
//
// The following backwards-compatibility guidelines apply:
//
// * Adding this annotation to an unannotated method is backwards
// compatible.
// * Adding this annotation to a method which already has existing
// method signature annotations is backwards compatible if and only if
// the new method signature annotation is last in the sequence.
// * Modifying or removing an existing method signature annotation is
// a breaking change.
// * Re-ordering existing method signature annotations is a breaking
// change.
repeated string method_signature = 1051;
}
\ No newline at end of file
// Copyright 2019 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
//
// https://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.
syntax = "proto3";
package google.api;
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "FieldBehaviorProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// An indicator of the behavior of a given field (for example, that a field
// is required in requests, or given as output but ignored as input).
// This **does not** change the behavior in protocol buffers itself; it only
// denotes the behavior and may affect how API tooling handles the field.
//
// Note: This enum **may** receive new values in the future.
enum FieldBehavior {
// Conventional default for enums. Do not use this.
FIELD_BEHAVIOR_UNSPECIFIED = 0;
// Specifically denotes a field as optional.
// While all fields in protocol buffers are optional, this may be specified
// for emphasis if appropriate.
OPTIONAL = 1;
// Denotes a field as required.
// This indicates that the field **must** be provided as part of the request,
// and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
REQUIRED = 2;
// Denotes a field as output only.
// This indicates that the field is provided in responses, but including the
// field in a request does nothing (the server *must* ignore it and
// *must not* throw an error as a result of the field's presence).
OUTPUT_ONLY = 3;
// Denotes a field as input only.
// This indicates that the field is provided in requests, and the
// corresponding field is not included in output.
INPUT_ONLY = 4;
// Denotes a field as immutable.
// This indicates that the field may be set once in a request to create a
// resource, but may not be changed thereafter.
IMMUTABLE = 5;
}
extend google.protobuf.FieldOptions {
// A designation of a specific field behavior (required, output only, etc.)
// in protobuf messages.
//
// Examples:
//
// string name = 1 [(google.api.field_behavior) = REQUIRED];
// State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
// google.protobuf.Duration ttl = 1
// [(google.api.field_behavior) = INPUT_ONLY];
// google.protobuf.Timestamp expire_time = 1
// [(google.api.field_behavior) = OUTPUT_ONLY,
// (google.api.field_behavior) = IMMUTABLE];
repeated FieldBehavior field_behavior = 1052;
}
\ No newline at end of file
This diff is collapsed.
// Copyright 2020 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.
syntax = "proto3";
package google.api;
import "google/protobuf/any.proto";
option cc_enable_arenas = true;
option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody";
option java_multiple_files = true;
option java_outer_classname = "HttpBodyProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// 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.
message HttpBody {
// The HTTP Content-Type header value specifying the content type of the body.
string content_type = 1;
// The HTTP request/response body as raw binary.
bytes data = 2;
// Application specific response metadata. Must be set in the first response
// for streaming APIs.
repeated google.protobuf.Any extensions = 3;
}
This diff is collapsed.
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf;
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
option cc_enable_arenas = true;
option go_package = "google.golang.org/protobuf/types/known/timestamppb";
option java_package = "com.google.protobuf";
option java_outer_classname = "TimestampProto";
option java_multiple_files = true;
option objc_class_prefix = "GPB";
// A Timestamp represents a point in time independent of any time zone or local
// calendar, encoded as a count of seconds and fractions of seconds at
// nanosecond resolution. The count is relative to an epoch at UTC midnight on
// January 1, 1970, in the proleptic Gregorian calendar which extends the
// Gregorian calendar backwards to year one.
//
// All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
// second table is needed for interpretation, using a [24-hour linear
// smear](https://developers.google.com/time/smear).
//
// The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
// restricting to that range, we ensure that we can convert to and from [RFC
// 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
//
// # Examples
//
// Example 1: Compute Timestamp from POSIX `time()`.
//
// Timestamp timestamp;
// timestamp.set_seconds(time(NULL));
// timestamp.set_nanos(0);
//
// Example 2: Compute Timestamp from POSIX `gettimeofday()`.
//
// struct timeval tv;
// gettimeofday(&tv, NULL);
//
// Timestamp timestamp;
// timestamp.set_seconds(tv.tv_sec);
// timestamp.set_nanos(tv.tv_usec * 1000);
//
// Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
//
// FILETIME ft;
// GetSystemTimeAsFileTime(&ft);
// UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
//
// // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
// // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
// Timestamp timestamp;
// timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
// timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
//
// Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
//
// long millis = System.currentTimeMillis();
//
// Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
// .setNanos((int) ((millis % 1000) * 1000000)).build();
//
//
// Example 5: Compute Timestamp from Java `Instant.now()`.
//
// Instant now = Instant.now();
//
// Timestamp timestamp =
// Timestamp.newBuilder().setSeconds(now.getEpochSecond())
// .setNanos(now.getNano()).build();
//
//
// Example 6: Compute Timestamp from current time in Python.
//
// timestamp = Timestamp()
// timestamp.GetCurrentTime()
//
// # JSON Mapping
//
// In JSON format, the Timestamp type is encoded as a string in the
// [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
// format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
// where {year} is always expressed using four digits while {month}, {day},
// {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
// seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
// are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
// is required. A proto3 JSON serializer should always use UTC (as indicated by
// "Z") when printing the Timestamp type and a proto3 JSON parser should be
// able to accept both UTC and other timezones (as indicated by an offset).
//
// For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
// 01:30 UTC on January 15, 2017.
//
// In JavaScript, one can convert a Date object to this format using the
// standard
// [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
// method. In Python, a standard `datetime.datetime` object can be converted
// to this format using
// [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
// the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
// the Joda Time's [`ISODateTimeFormat.dateTime()`](
// http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D
// ) to obtain a formatter capable of generating timestamps in this format.
//
//
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive.
int32 nanos = 2;
}
\ No newline at end of file
package main
import (
"context"
"fmt"
"github.com/asim/go-micro/plugins/client/grpc/v4"
"github.com/asim/go-micro/plugins/registry/consul/v4"
"github.com/gin-gonic/gin"
"github.com/panjf2000/ants/v2"
"github.com/spf13/cobra"
"go-micro.dev/v4"
"go-micro.dev/v4/client"
"go-micro.dev/v4/registry"
"go-micro.dev/v4/selector"
"net/http"
"os"
"sync"
)
var (
port string // http监听端口
consulAddr []string // consul注册中心地址
microRegistry registry.Registry // 注册中心实例
microClient client.Client // 客户端实例
pool *ants.Pool // 协程池, 并发查询GetService用
)
// http响应对象
type response struct {
Message string `json:"message"`
Result interface{} `json:"result"`
}
func main() {
pool, _ = ants.NewPool(16) // 协程池, 并发查询GetService用
defer pool.Release()
cmd := &cobra.Command{
Use: "go run gateway.go或go build -o .",
Short: "go-micro开发调试网关 转发http->grpc, 例如: post请求 /service.demo/Demo.GetDemo",
Run: func(cmd *cobra.Command, args []string) {
start()
},
}
// 解析命令行参数
cmd.Flags().StringVarP(&port, "port", "p", "8100", "监听端口")
cmd.Flags().StringSliceVarP(&consulAddr, "consul", "c", []string{}, "consul注册中心地址,多个则逗号隔开")
cmd.MarkFlagRequired("consul")
err := cmd.Execute()
if err != nil {
os.Exit(0)
}
}
func start() {
microRegistry = consul.NewRegistry(registry.Addrs(consulAddr...))
// 轮询访问
newSelector := selector.NewSelector(
selector.Registry(microRegistry),
selector.SetStrategy(selector.RoundRobin),
)
service := micro.NewService(
micro.Client(grpc.NewClient()),
micro.Selector(newSelector),
//micro.WrapClient(NewLogWrapper),
)
microClient = service.Client()
// gin http服务
r := gin.Default()
r.GET("/list", listService)
r.POST("/:service/:endpoint", callService)
r.Run(fmt.Sprintf(":%s", port))
}
// 列出注册的服务
func listService(ctx *gin.Context) {
srvs, err := microRegistry.ListServices()
if err != nil {
ctx.JSON(http.StatusOK, &response{Message: err.Error()})
return
}
// 组装响应信息
type serviceInfo struct {
Name string `json:"name"`
Endpoints []string `json:"endpoints"`
Nodes []*registry.Node `json:"nodes"`
}
resultChan := make(chan *serviceInfo, 100)
wg := &sync.WaitGroup{}
var services = make(map[string][]*serviceInfo)
for _, srv := range srvs {
wg.Add(1)
tempName := srv.Name
pool.Submit(func() {
defer wg.Done()
srvDetail, _ := microRegistry.GetService(tempName)
for _, v := range srvDetail {
s := &serviceInfo{
Name: tempName,
Nodes: v.Nodes,
}
e := make([]string, 0)
for _, ee := range v.Endpoints {
e = append(e, ee.Name)
}
s.Endpoints = e
resultChan <- s
}
})
}
go func() {
wg.Wait()
close(resultChan)
}()
for v := range resultChan {
services[v.Name] = append(services[v.Name], v)
}
ctx.JSON(http.StatusOK, &response{Message: "ok", Result: services})
}
// 调用服务
func callService(ctx *gin.Context) {
var requestData map[string]interface{}
err := ctx.ShouldBindJSON(&requestData)
if err != nil {
ctx.JSON(http.StatusOK, &response{Message: fmt.Sprintf("获取body参数失败: %s", err)})
return
}
//req := cli.NewRequest("service.demo", "Demo.GetDemo", requestData, mClient.WithContentType("application/json"))
req := microClient.NewRequest(ctx.Param("service"), ctx.Param("endpoint"), requestData, client.WithContentType("application/json"))
var res map[string]interface{}
err = microClient.Call(context.Background(), req, &res)
if err != nil {
ctx.JSON(http.StatusOK, &response{
Message: "grpc响应异常",
Result: err,
})
return
}
ctx.JSON(http.StatusOK, &response{
Message: "ok",
Result: res,
})
}
module example
go 1.18
require (
github.com/asim/go-micro/plugins/client/grpc/v4 v4.0.0-20220511085541-13b76331ec6f
github.com/asim/go-micro/plugins/registry/consul/v4 v4.0.0-20220511085541-13b76331ec6f
github.com/gin-gonic/gin v1.7.7
github.com/spf13/cobra v1.4.0
go-micro.dev/v4 v4.7.0
)
require (
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20220517143526-88bb52951d5b // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/armon/go-metrics v0.4.0 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.3.1 // indirect
github.com/go-git/go-git/v5 v5.4.2 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/hashicorp/consul/api v1.12.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/serf v0.9.8 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/miekg/dns v1.1.49 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/panjf2000/ants/v2 v2.5.0 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/urfave/cli/v2 v2.8.1 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 // indirect
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/genproto v0.0.0-20220526192754-51939a95c655 // indirect
google.golang.org/grpc v1.46.2 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment