Commit 74f7eeea authored by kzkzzzz's avatar kzkzzzz

feat: init

parents
Pipeline #1226 canceled with stages
.idea
.vscode
tmp
*.log
*.yaml
.DS_Store
\ No newline at end of file
# 参考第三代微服务方案 Service Mesh
#### 不依赖go-micro,kit等微服务框架, 不依赖外部注册中心etcd, consul服务发现
#### 而通过k8s + grpc + isito或linkerd
---
### common 存放公共依赖库
### service 具体每一个微服务,一个文件夹一个服务
#### api 存放proto等文件
#### config 存放配置模板文件
#### internal 内部逻辑
#### conf 处理配置相关
#### dao层 查询数据库
#### model层 定义数据库表结构体, 以及相关扩展参数结构体
#### service层 实现逻辑
#### server grpc,http等服务启动入口
---
其他目录按需调整扩展
例如pkg,helper,tool等封装内置工具, 如果共用的最好放在最外层common
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 logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"log"
"os"
"strings"
)
var (
zapLogger *zap.Logger
zapSug *zap.SugaredLogger
)
var levelMap = map[string]zapcore.Level{
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel,
"error": zapcore.ErrorLevel,
"dpanic": zapcore.DPanicLevel,
"panic": zapcore.PanicLevel,
"fatal": zapcore.FatalLevel,
}
type Config struct {
Filename string
Console bool
MaxSize int // megabytes
MaxBackups int
MaxAge int //days
Compress bool // disabled by default
Level string
}
func Init(conf *Config) {
ws := make([]zapcore.WriteSyncer, 0)
if conf.Console || conf.Filename == "" {
ws = append(ws, zapcore.AddSync(os.Stdout))
}
if conf.Filename != "" {
s := &lumberjack.Logger{
Filename: conf.Filename,
MaxSize: conf.MaxSize, // 拆分大小, megabytes
MaxBackups: conf.MaxBackups,
MaxAge: conf.MaxAge, //days
Compress: conf.Compress, // disabled by default
}
ws = append(ws, zapcore.AddSync(s))
}
var level zapcore.Level
if v, ok := levelMap[strings.ToLower(conf.Level)]; ok {
level = v
} else {
level = zapcore.DebugLevel
}
zapLogger, zapSug = NewLogger(level, ws...)
}
func NewLogger(level zapcore.Level, ws ...zapcore.WriteSyncer) (l *zap.Logger, s *zap.SugaredLogger) {
cfg := zap.NewProductionEncoderConfig()
//cfg.EncodeTime = zapcore.ISO8601TimeEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
encoder := zapcore.NewConsoleEncoder(cfg)
core := zapcore.NewCore(encoder, zapcore.NewMultiWriteSyncer(ws...), level)
l = zap.New(zapcore.NewTee(core), zap.AddCaller())
s = l.Sugar()
return
}
func GetStdLog() *log.Logger {
return zap.NewStdLog(zapLogger)
}
func With(args ...interface{}) *zap.SugaredLogger {
return zapSug.With(args...)
}
func Debug(args ...interface{}) {
zapSug.Debug(args...)
}
func Info(args ...interface{}) {
zapSug.Info(args...)
}
func Warn(args ...interface{}) {
zapSug.Warn(args...)
}
func Error(args ...interface{}) {
zapSug.Error(args...)
}
func Fatal(args ...interface{}) {
zapSug.Fatal(args...)
}
func Debugf(format string, args ...interface{}) {
zapSug.Debugf(format, args...)
}
// Infof Printf calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Printf.
func Infof(format string, args ...interface{}) {
zapSug.Infof(format, args...)
}
func Warnf(format string, args ...interface{}) {
zapSug.Warnf(format, args...)
}
func Errorf(format string, args ...interface{}) {
zapSug.Errorf(format, args...)
}
func Fatalf(format string, args ...interface{}) {
zapSug.Fatalf(format, args...)
}
func Flush() {
zapLogger.Sync()
zapSug.Sync()
}
package mysql
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"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
db, err = gorm.Open(mysql.Open(conf.Dsn), &gorm.Config{
//Logger: NewDBLogger(logger2.GetStdLog()),
})
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)
if conf.Debug {
db = db.Debug()
}
return
}
func NewDBLogger(writer logger.Writer) logger.Interface {
return logger.New(writer, logger.Config{
SlowThreshold: time.Second * 5,
Colorful: true,
IgnoreRecordNotFoundError: true,
LogLevel: logger.Warn,
})
}
package server
const DevMode = "dev"
const ProdMode = "prod"
type Config struct {
Name string
Mode string
GrpcAddr string
RegisterAddr string
}
module grpc-base
go 1.18
require (
github.com/spf13/cast v1.5.0
github.com/spf13/viper v1.12.0
go.uber.org/zap v1.17.0
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
google.golang.org/grpc v1.46.2
google.golang.org/protobuf v1.28.0
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gorm.io/driver/mysql v1.3.3
gorm.io/gorm v1.23.5
)
require (
github.com/BurntSushi/toml v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // 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/subosito/gotenv v1.3.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/net v0.0.0-20220526153639-5463443f8c37 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220527130721-00d5c0f3be58 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
)
This diff is collapsed.
# air开发时热更新重启项目 https://github.com/cosmtrek/air
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 500
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 = 200
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
SHELL = /bin/sh
.PHONY: init
init:
@go get -u google.golang.org/protobuf/proto
@go install github.com/golang/protobuf/protoc-gen-go@latest
@go install github.com/asim/go-micro/cmd/protoc-gen-micro/v4@latest
.PHONY: proto
proto:
@protoc -I ./api \
--go_out=:./api --go_opt=paths=source_relative \
--go-grpc_out=:./api --go-grpc_opt=paths=source_relative \
./api/*.proto
# --go-grpc_out=./proto --go-grpc_opt=paths=source_relative \
# --grpc-gateway_out=./proto --grpc-gateway_opt=paths=source_relative # grpc-gateway网关参数
.PHONY: update
update:
@go get -u
.PHONY: tidy
tidy:
@go mod tidy
.PHONY: build
build:
@go build -x -v -o tmp/ main.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.20.1
// source: testuser.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type UserInfoReq struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *UserInfoReq) Reset() {
*x = UserInfoReq{}
if protoimpl.UnsafeEnabled {
mi := &file_testuser_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UserInfoReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserInfoReq) ProtoMessage() {}
func (x *UserInfoReq) ProtoReflect() protoreflect.Message {
mi := &file_testuser_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UserInfoReq.ProtoReflect.Descriptor instead.
func (*UserInfoReq) Descriptor() ([]byte, []int) {
return file_testuser_proto_rawDescGZIP(), []int{0}
}
func (x *UserInfoReq) GetId() string {
if x != nil {
return x.Id
}
return ""
}
type UserInfoResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Age int32 `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *UserInfoResp) Reset() {
*x = UserInfoResp{}
if protoimpl.UnsafeEnabled {
mi := &file_testuser_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *UserInfoResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UserInfoResp) ProtoMessage() {}
func (x *UserInfoResp) ProtoReflect() protoreflect.Message {
mi := &file_testuser_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UserInfoResp.ProtoReflect.Descriptor instead.
func (*UserInfoResp) Descriptor() ([]byte, []int) {
return file_testuser_proto_rawDescGZIP(), []int{1}
}
func (x *UserInfoResp) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *UserInfoResp) GetAge() int32 {
if x != nil {
return x.Age
}
return 0
}
func (x *UserInfoResp) GetName() string {
if x != nil {
return x.Name
}
return ""
}
var File_testuser_proto protoreflect.FileDescriptor
var file_testuser_proto_rawDesc = []byte{
0x0a, 0x0e, 0x74, 0x65, 0x73, 0x74, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x22, 0x1d, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22,
0x44, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12,
0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x38, 0x0a, 0x08, 0x54, 0x65, 0x73, 0x74, 0x55, 0x73, 0x65,
0x72, 0x12, 0x2c, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f,
0x12, 0x0c, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x1a, 0x0d,
0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x42,
0x11, 0x5a, 0x0f, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61,
0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_testuser_proto_rawDescOnce sync.Once
file_testuser_proto_rawDescData = file_testuser_proto_rawDesc
)
func file_testuser_proto_rawDescGZIP() []byte {
file_testuser_proto_rawDescOnce.Do(func() {
file_testuser_proto_rawDescData = protoimpl.X.CompressGZIP(file_testuser_proto_rawDescData)
})
return file_testuser_proto_rawDescData
}
var file_testuser_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_testuser_proto_goTypes = []interface{}{
(*UserInfoReq)(nil), // 0: UserInfoReq
(*UserInfoResp)(nil), // 1: UserInfoResp
}
var file_testuser_proto_depIdxs = []int32{
0, // 0: TestUser.GetUserInfo:input_type -> UserInfoReq
1, // 1: TestUser.GetUserInfo:output_type -> UserInfoResp
1, // [1:2] is the sub-list for method output_type
0, // [0:1] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_testuser_proto_init() }
func file_testuser_proto_init() {
if File_testuser_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_testuser_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UserInfoReq); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_testuser_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UserInfoResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_testuser_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_testuser_proto_goTypes,
DependencyIndexes: file_testuser_proto_depIdxs,
MessageInfos: file_testuser_proto_msgTypes,
}.Build()
File_testuser_proto = out.File
file_testuser_proto_rawDesc = nil
file_testuser_proto_goTypes = nil
file_testuser_proto_depIdxs = nil
}
syntax = "proto3";
option go_package = "./proto/api;api";
message UserInfoReq {
string id = 1;
}
message UserInfoResp {
int64 id = 1;
int32 age = 2;
string name = 3;
}
service TestUser {
rpc GetUserInfo(UserInfoReq) returns (UserInfoResp){};
}
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.20.1
// source: testuser.proto
package api
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// TestUserClient is the client API for TestUser service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type TestUserClient interface {
GetUserInfo(ctx context.Context, in *UserInfoReq, opts ...grpc.CallOption) (*UserInfoResp, error)
}
type testUserClient struct {
cc grpc.ClientConnInterface
}
func NewTestUserClient(cc grpc.ClientConnInterface) TestUserClient {
return &testUserClient{cc}
}
func (c *testUserClient) GetUserInfo(ctx context.Context, in *UserInfoReq, opts ...grpc.CallOption) (*UserInfoResp, error) {
out := new(UserInfoResp)
err := c.cc.Invoke(ctx, "/TestUser/GetUserInfo", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// TestUserServer is the server API for TestUser service.
// All implementations must embed UnimplementedTestUserServer
// for forward compatibility
type TestUserServer interface {
GetUserInfo(context.Context, *UserInfoReq) (*UserInfoResp, error)
mustEmbedUnimplementedTestUserServer()
}
// UnimplementedTestUserServer must be embedded to have forward compatible implementations.
type UnimplementedTestUserServer struct {
}
func (UnimplementedTestUserServer) GetUserInfo(context.Context, *UserInfoReq) (*UserInfoResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserInfo not implemented")
}
func (UnimplementedTestUserServer) mustEmbedUnimplementedTestUserServer() {}
// UnsafeTestUserServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to TestUserServer will
// result in compilation errors.
type UnsafeTestUserServer interface {
mustEmbedUnimplementedTestUserServer()
}
func RegisterTestUserServer(s grpc.ServiceRegistrar, srv TestUserServer) {
s.RegisterService(&TestUser_ServiceDesc, srv)
}
func _TestUser_GetUserInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UserInfoReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TestUserServer).GetUserInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/TestUser/GetUserInfo",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TestUserServer).GetUserInfo(ctx, req.(*UserInfoReq))
}
return interceptor(ctx, in, info, handler)
}
// TestUser_ServiceDesc is the grpc.ServiceDesc for TestUser service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var TestUser_ServiceDesc = grpc.ServiceDesc{
ServiceName: "TestUser",
HandlerType: (*TestUserServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetUserInfo",
Handler: _TestUser_GetUserInfo_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "testuser.proto",
}
# 开发环境配置
server:
name: "service.testuser"
mode: dev
grpcAddr: ":9900"
logger:
level: debug
console: false # 打印到控制台
filename: "server.log" # 空则不写入文件, 只打印到控制台
maxSize: 100 # 日志文件大小,单位MB
maxBackups: 0 # 备份数
maxAge: 0 # days
compress: true # disabled by default
mysql:
dsn: "remote:admin666@tcp(127.0.0.1:3306)/test?loc=Local&charset=utf8mb4"
maxConn: 8
maxIdleConn: 2
maxLifetime: 1800 # 连接有效时间,单位秒
debug: true
\ No newline at end of file
# 生产环境配置
server:
name: "service.testuser"
mode: dev
grpcAddr: ":9900"
logger:
level: info
console: true # 打印到控制台
filename: "" # 空则不写入文件, 只打印到控制台
maxSize: 100 # 日志文件大小,单位MB
maxBackups: 0 # 备份数, 0所有
maxAge: 0 # 日志保持的天数, 0永久
compress: true # 是否压缩
mysql:
dsn: "remote:admin666@tcp(127.0.0.1:3306)/test?loc=Local&charset=utf8mb4"
maxConn: 8
maxIdleConn: 2
maxLifetime: 1800 # 连接有效时间,单位秒
debug: false
package conf
import (
"flag"
"grpc-base/common/conf"
"grpc-base/common/logger"
"grpc-base/common/mysql"
"grpc-base/common/server"
)
func init() {
// 读取命令, 配置文件参数, 不指定则当前路径server.yaml
flag.StringVar(&ConfPath, "conf", "server.yaml", "配置文件路径")
}
var (
ConfPath string
Conf = &Config{}
)
type Config struct {
Server *server.Config
Log *logger.Config
Mysql *mysql.Config
}
func LoadConfig() {
conf.LoadFromYaml(ConfPath, &Conf)
}
package dao
import (
"gorm.io/gorm"
"grpc-base/common/mysql"
"grpc-base/service/testuser/internal/conf"
"grpc-base/service/testuser/internal/model"
)
type Dao struct {
db *gorm.DB
}
func New(c *conf.Config) (d *Dao) {
d = &Dao{
db: mysql.NewDB(c.Mysql),
}
return
}
func (d *Dao) GetMemberById(uid int, col []string) (res *model.User, err error) {
res = &model.User{}
//d.db.Table("image2").Limit(1).Take(&res)
err = d.db.Table("user").
Select(col).
Where("id = ?", uid).Take(res).Error
return
}
package model
type User struct {
Id int64 `json:"id"` // Id
Age int64 `json:"age"` // Age
Name string `json:"name"` // Name
}
func (u *User) TableName() string {
return "user"
}
package server
import (
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"grpc-base/common/logger"
"grpc-base/service/testuser/api"
"grpc-base/service/testuser/internal/conf"
"grpc-base/service/testuser/internal/service"
"net"
)
var server *grpc.Server
func NewGrpcServer(conf *conf.Config, svc *service.TestUser) error {
if conf.Server.GrpcAddr == "" {
conf.Server.GrpcAddr = ":50000"
}
listen, err := net.Listen("tcp", conf.Server.GrpcAddr)
if err != nil {
return err
}
server = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
recoverHandler,
logBeforeReq,
),
)
api.RegisterTestUserServer(server, svc)
logger.Infof("listen grpc srv on %s\n", conf.Server.GrpcAddr)
reflection.Register(server)
err = server.Serve(listen)
if err != nil {
return err
}
return nil
}
func GracefulStopGrpc() {
server.GracefulStop()
logger.Warn("grpc server stop")
}
package server
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"grpc-base/common/logger"
"grpc-base/service/testuser/api"
"runtime/debug"
)
// 捕获异常
func recoverHandler(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
defer func() {
if err2 := recover(); err2 != nil {
logger.Errorf("触发panic, recover: %v\n%s\n", err2, debug.Stack())
err = status.Errorf(codes.Internal, "触发panic: %v", err2)
}
}()
return handler(ctx, req)
}
// 记录请求信息
func logBeforeReq(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if v, ok := req.(*api.UserInfoReq); ok {
logger.Debugf("request: %s data: %v\n", info.FullMethod, v)
}
return handler(ctx, req)
}
package service
import (
"grpc-base/service/testuser/api"
"grpc-base/service/testuser/internal/model"
)
func UserModelToResp(m *model.User) (res *api.UserInfoResp) {
return &api.UserInfoResp{
Id: int64(m.Id),
Age: int32(m.Id),
Name: m.Name,
}
}
package service
import (
context "context"
"github.com/spf13/cast"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"grpc-base/service/testuser/api"
"grpc-base/service/testuser/internal/dao"
)
var _ api.TestUserServer = &TestUser{}
type TestUser struct {
api.UnimplementedTestUserServer
dao *dao.Dao
}
func (s TestUser) GetUserInfo(ctx context.Context, req *api.UserInfoReq) (*api.UserInfoResp, error) {
info, err := s.dao.GetMemberById(cast.ToInt(req.Id), []string{"*"})
if err != nil {
return nil, status.Errorf(codes.NotFound, "查询失败: %s", err.Error())
}
return UserModelToResp(info), nil
}
func New(d *dao.Dao) *TestUser {
return &TestUser{
dao: d,
}
}
package main
import (
"flag"
"fmt"
"golang.org/x/sync/errgroup"
"grpc-base/common/logger"
"grpc-base/service/testuser/internal/conf"
"grpc-base/service/testuser/internal/dao"
"grpc-base/service/testuser/internal/server"
"grpc-base/service/testuser/internal/service"
"os"
"os/signal"
"syscall"
)
func main() {
flag.Parse()
conf.LoadConfig()
logger.Init(conf.Conf.Log)
defer logger.Flush()
d := dao.New(conf.Conf)
svc := service.New(d)
eg := errgroup.Group{}
eg.Go(func() error {
return server.NewGrpcServer(conf.Conf, svc)
})
eg.Go(func() error {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
v := <-ch
fmt.Printf("\n捕获信号: %v\n", v)
//time.Sleep(time.Millisecond * 600)
server.GracefulStopGrpc()
return nil
})
logger.Infof("%s", "server start")
if err := eg.Wait(); err != nil {
logger.Error(err)
}
logger.Info("end")
}
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