├── .gitignore ├── README.md ├── api ├── api.go ├── caches │ └── README.md ├── client │ └── client.go ├── controllers │ └── ctr-comic │ │ └── ctr-comic.go ├── logs │ └── README.md ├── models │ └── README.md └── routers │ └── router.go ├── conf ├── conf.go ├── conf_dev.toml └── conf_pro.toml ├── core ├── elastic.go └── mysql.go ├── glide.yaml ├── pb ├── srvcomic.pb.go └── srvcomic.proto ├── rpc.go ├── service ├── register.go └── srv-comic │ └── srv-comic.go └── utils ├── utils_file.go ├── utils_http.go ├── utils_log.go ├── utils_map.go └── utils_string.go /.gitignore: -------------------------------------------------------------------------------- 1 | github.com 2 | golang.org 3 | google.golang.org 4 | gopkg.in 5 | glide.lock 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # golang-grpc-gin 2 | 3 | ## 简介 4 | 5 | 本程序是一个 简陋的 demo ,没有业务就只是为了打通rpc与gin 数据库操作,用来学习grpc,gin 框架所写。 6 | 7 | grpc 提供服务,gin调用服务从而提供 RESTfull Api, demo还非常简陋,后面继续完善。 8 | 9 | 10 | ## 特性 11 | 12 | - 1.rpc : 采用 grpc 框架 实现 rpc 服务调用 13 | 14 | - 2.api : 采用 gin 框架 实现RESTfull api 15 | 16 | - 3.db : 采用 xorm 操作 mysql 实现分库,主从 17 | 18 | - 4.package : 采用 glide 实现包管理 19 | 20 | - 5.全文检索引擎 : 采用es 实现es的配置以及相关操作 21 | 22 | 23 | ## 目录结构 24 | 25 | ``` 26 | . 27 | ├── README.md 28 | ├── api Api 应用 29 | │   ├── api.go 应用入口 30 | │   ├── caches 31 | │   ├── client 32 | │   ├── controllers 33 | │   ├── logs 34 | │   ├── models 35 | │   └── routers 36 | ├── conf 配置 37 | │   ├── conf.go 38 | │   ├── conf_dev.toml 39 | │   └── conf_pro.toml 40 | ├── core module 41 | │   └── model.go 42 | ├── glide.lock 43 | ├── glide.yaml 44 | ├── pb protobuf 描述文件目录 45 | │   ├── srvcomic.pb.go 46 | │   ├── srvcomic.proto 47 | │   └── srvnews.proto 48 | ├── rpc.go RPC应用入口 49 | ├── service 服务 50 | │   ├── register.go 服务注册 51 | │   ├── srv-comic 52 | │   └── srv-news 53 | ├── utils 工具函数 54 | │   ├── utils_log.go 55 | │   └── utils_string.go 56 | └── vendor 第三方包 57 | ``` 58 | 59 | ## 封装的内容 60 | 61 | ### grpc部分 62 | 63 | - 1.拦截器 64 | 65 | 服务端: 可以理解成gin中的 middleware ,其他语言中的 hook。实现 异常的捕获,恢复防止程序意外退出。以及函数栈执行时间。 66 | 67 | 客户端: 拦截器 执行时间, 连接池(虽然http2已经足够快保持长链接,这里也手动实现下) 68 | 69 | - 2.trace 70 | 71 | 利用golang 标准库 trace 来实现 服务调用的跟踪,如果想更详细的trace可以结合 zipkin。 72 | 73 | - 3.mysql 74 | 75 | 采用了xorm 实现通过主从可以操作多个数据库。 76 | 77 | - 4.配置加载 78 | 79 | 采用 toml 来对配置的解码,针对 开发环境 生产环境 加载不同配置 80 | 81 | - 5.es 82 | 83 | es主从配置的封装等 84 | 85 | 86 | ### api部分 87 | 88 | - 1.middleware 89 | 90 | jwt 验证,recover 异常的捕获与恢复,防止程序退出。 91 | 92 | MVC 目录划分 93 | 94 | 路由分组规划(私有以及公开路由) 95 | 96 | 97 | ## 数据表 98 | 99 | 只是简单的几个字段。 100 | 101 | ``` 102 | 103 | DROP TABLE IF EXISTS `comic_info`; 104 | 105 | CREATE TABLE `comic_info` ( 106 | `comic_id` int(11) unsigned NOT NULL AUTO_INCREMENT, 107 | `comic_name` varchar(32) NOT NULL DEFAULT '' COMMENT '名字', 108 | `status` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '1:正常2:关闭', 109 | `create_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '时间', 110 | PRIMARY KEY (`comic_id`) 111 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='漫画'; 112 | 113 | LOCK TABLES `comic_info` WRITE; 114 | /*!40000 ALTER TABLE `comic_info` DISABLE KEYS */; 115 | 116 | INSERT INTO `comic_info` (`comic_id`, `comic_name`, `status`, `create_time`) 117 | VALUES 118 | (1,'啊 啊啊啊',1,12121212), 119 | (2,'分水电费水电费',1,111110), 120 | (3,'吞吞吐吐',2,9999); 121 | 122 | ``` 123 | 124 | ## 使用 125 | 126 | 前提是 对golang有所熟悉, 比如环境搭,工具链的使用。 127 | 128 | 1.git clone https://github.com/pzhen/golang-grpc-gin.git ./gogrpcgin 执行 glide up 将类包更新下来(需要vpn,你懂得) 129 | 130 | 2.将数据表导入mysql 131 | 132 | 3.然后修改配置文件中的数据库信息。 133 | 134 | 4.分别运行 go run rpc.go | go run api.go 135 | 136 | 5.访问 http://localhost:8080/rpc/comic/test 可以看到数据,命令行查看调用栈时间 137 | 138 | 可以手动抛出 panic ,来验证拦截器。 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "gogrpcgin/conf" 5 | "gogrpcgin/api/routers" 6 | ) 7 | 8 | // dev/pro 9 | const Env = "dev" 10 | 11 | func main() { 12 | conf.InitConfig(Env,"api") 13 | routers.InitRun() 14 | } 15 | 16 | -------------------------------------------------------------------------------- /api/caches/README.md: -------------------------------------------------------------------------------- 1 | cache -------------------------------------------------------------------------------- /api/client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import( 4 | "sync" 5 | "google.golang.org/grpc" 6 | "gogrpcgin/conf" 7 | "time" 8 | "context" 9 | "gogrpcgin/utils" 10 | ) 11 | 12 | 13 | func NewRpcConn() *grpc.ClientConn{ 14 | 15 | var address = conf.Conf.App.Rpc.RpcAddr 16 | 17 | p := &sync.Pool{ 18 | New: func() interface {}{ 19 | conn,_ := grpc.Dial(address, grpc.WithInsecure(),WithClientDurativeInterceptor()) 20 | return conn 21 | }, 22 | } 23 | 24 | return p.Get().(*grpc.ClientConn) 25 | } 26 | 27 | func WithClientDurativeInterceptor() grpc.DialOption { 28 | 29 | return grpc.WithUnaryInterceptor(clientDurativeInterceptorHandle) 30 | } 31 | 32 | func clientDurativeInterceptorHandle( 33 | ctx context.Context, 34 | method string, 35 | req interface{}, 36 | reply interface{}, 37 | cc *grpc.ClientConn, 38 | invoker grpc.UnaryInvoker, 39 | opts ...grpc.CallOption, 40 | ) error { 41 | 42 | start := time.Now() 43 | err := invoker(ctx, method, req, reply, cc, opts...) 44 | utils.LogPrint("invoke remote method=%s duration=%s error=%v", method, time.Since(start), err) 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /api/controllers/ctr-comic/ctr-comic.go: -------------------------------------------------------------------------------- 1 | package ctr_comic 2 | 3 | import ( 4 | "gogrpcgin/api/client" 5 | "gogrpcgin/pb" 6 | "github.com/gin-gonic/gin" 7 | "gogrpcgin/core" 8 | ) 9 | 10 | 11 | func Test(c *gin.Context) { 12 | 13 | res,_ :=pb.NewComicServiceClient(client.NewRpcConn()).GetComicListByPage(c, &pb.ComicFilterRequest{Status:1}) 14 | 15 | var user core.EsResponse 16 | core.MasterES("author","author").Query(`{"query": {"bool": {"should": {"regexp": {"sina_nickname": ".*果.*"}}}}}`,&user) 17 | 18 | c.JSON(200,gin.H{ 19 | "code":1, 20 | "message":"ok", 21 | "data":res, 22 | "data2":user, 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /api/logs/README.md: -------------------------------------------------------------------------------- 1 | log -------------------------------------------------------------------------------- /api/models/README.md: -------------------------------------------------------------------------------- 1 | model 层 -------------------------------------------------------------------------------- /api/routers/router.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import( 4 | "github.com/gin-gonic/gin" 5 | "gogrpcgin/conf" 6 | "gogrpcgin/api/controllers/ctr-comic" 7 | "time" 8 | jwt_lib "github.com/dgrijalva/jwt-go" 9 | "github.com/gin-gonic/contrib/jwt" 10 | "github.com/ekyoung/gin-nice-recovery" 11 | "github.com/DeanThompson/ginpprof" 12 | ) 13 | 14 | func InitRun() { 15 | 16 | engine := gin.Default() 17 | //http://127.0.0.1:8080/debug/pprof/ 18 | ginpprof.Wrap(engine) 19 | 20 | engine.Use(nice.Recovery(recoveryHandler)) 21 | 22 | // 公开路由 23 | public := engine.Group("/rpc") 24 | { 25 | public.POST("/auth", createTokenHandle) 26 | public.GET("/comic/test",ctr_comic.Test) 27 | } 28 | 29 | // 非公开路由 30 | private := engine.Group("/rpc/private") 31 | { 32 | private.Use(jwt.Auth(conf.Conf.App.Api.ApiSecretKey)) 33 | 34 | private.POST("/comic/show", func(c *gin.Context) { 35 | //library.ProcessRPC(c, &c_comic.ComicRPC{}) 36 | }) 37 | 38 | private.GET("/test",func(c *gin.Context) { 39 | panic("sssssssss") 40 | c.JSON(200, gin.H{"message": "Hello from private"}) 41 | }) 42 | } 43 | 44 | 45 | 46 | 47 | // service run 48 | if conf.Conf.App.Api.ApiTls == true { 49 | engine.RunTLS(conf.Conf.App.Api.ApiTlsAddr,"keys/server.crt","keys/server.key") 50 | }else { 51 | engine.Run(conf.Conf.App.Api.ApiAddr) 52 | } 53 | } 54 | 55 | // 生成 token 56 | func createTokenHandle(c *gin.Context) { 57 | 58 | username := c.PostForm("username") 59 | password := c.PostForm("password") 60 | 61 | if username == conf.Conf.App.Api.ApiUsername && password == conf.Conf.App.Api.ApiPassword { 62 | // Create the token 63 | token := jwt_lib.New(jwt_lib.GetSigningMethod("HS256")) 64 | // Set some claims 65 | token.Claims = jwt_lib.MapClaims{ 66 | "Id": "Christopher", 67 | "exp": time.Now().Add(time.Hour * 1).Unix(), 68 | } 69 | // Sign and get the complete encoded token as a string 70 | tokenString, err := token.SignedString([]byte(conf.Conf.App.Api.ApiSecretKey)) 71 | if err != nil { 72 | c.JSON(500, gin.H{"code":0, "message": "Could not generate token"}) 73 | return 74 | } 75 | c.JSON(200, gin.H{"code":0,"message":"ok","data":gin.H{"token":tokenString}}) 76 | return 77 | }else { 78 | 79 | c.JSON(200, gin.H{"code":0, "message": "username or password is wrong"}) 80 | return 81 | } 82 | 83 | 84 | } 85 | 86 | // 捕捉 panic 87 | func recoveryHandler(c *gin.Context, err interface{}) { 88 | c.JSON(500, gin.H{"code":0, "message": err}) 89 | } -------------------------------------------------------------------------------- /conf/conf.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "os" 5 | "io/ioutil" 6 | "github.com/BurntSushi/toml" 7 | "gogrpcgin/utils" 8 | ) 9 | 10 | var ( 11 | // holds the global app config. 12 | Conf config 13 | ) 14 | 15 | type config struct { 16 | // 应用配置 17 | App app 18 | 19 | // MySQL 20 | DB database `toml:"database"` 21 | 22 | // Es 23 | ES elasticsearch `toml:"elasticsearch"` 24 | 25 | // 静态资源 26 | Static static 27 | 28 | // Redis 29 | Redis redis 30 | 31 | // Memcached 32 | Memcached memcached 33 | 34 | } 35 | 36 | // 项目配置 37 | type app struct { 38 | 39 | Api struct{ 40 | ApiName string `toml:"apiName"` 41 | ApiVersion string `toml:"apiVersion"` 42 | ApiAddr string `toml:"apiAddr"` 43 | ApiTls bool `toml:"apiTls"` 44 | ApiTlsAddr string `toml:"apiTlsAddr"` 45 | ApiSecretKey string `toml:"apiSecretKey"` 46 | ApiUsername string `toml:"apiUsername"` 47 | ApiPassword string `toml:"apiPassword"` 48 | ApiLogFile string `toml:"apiLogFile"` 49 | } 50 | 51 | Rpc struct{ 52 | RpcName string `toml:"rpcName"` 53 | RpcVersion string `toml:"rpcVersion"` 54 | RpcAddr string `toml:"rpcAddr"` 55 | RpcTraceAddr string `toml:"rpcTraceAddr"` 56 | RpcLogFile string `toml:"rpcLogFile"` 57 | } 58 | } 59 | 60 | type static struct { 61 | 62 | } 63 | 64 | type database map[string]map[string][]string 65 | 66 | type elasticsearch map[string]map[string][]string 67 | 68 | type redis struct { 69 | 70 | Server string `toml:"server"` 71 | Pwd string `toml:"pwd"` 72 | } 73 | 74 | type memcached struct { 75 | 76 | Server string `toml:"server"` 77 | } 78 | 79 | func InitConfig(env string,app string) { 80 | 81 | if env == "" { 82 | env = "dev" 83 | } 84 | 85 | var configFile string 86 | if app == "api" { 87 | configFile = "../conf/conf_"+env+".toml" 88 | } 89 | if app == "rpc" { 90 | configFile = "conf/conf_"+env+".toml" 91 | } 92 | 93 | if _, err := os.Stat(configFile); err != nil { 94 | utils.LogFatalfError(err) 95 | } else { 96 | 97 | configBytes, err := ioutil.ReadFile(configFile) 98 | if err != nil { 99 | utils.LogFatalfError(err) 100 | } 101 | 102 | _, err = toml.Decode(string(configBytes), &Conf) 103 | if err != nil { 104 | utils.LogFatalfError(err) 105 | } 106 | 107 | utils.LogPrint("Load config from file : %s", configFile) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /conf/conf_dev.toml: -------------------------------------------------------------------------------- 1 | # servic 配置 修改了配置不要忘记修改struct 2 | [app.api] 3 | apiName = "wbcomic api" 4 | apiVersion = "1.0" 5 | 6 | # http 7 | apiAddr = "127.0.0.1:8080" 8 | 9 | # https 10 | apiTls = false 11 | apiTlsAddr = "127.0.0.1:8081" 12 | 13 | # jwt auth 14 | apiSecretKey = "12345!@#$%" 15 | apiUsername = "demo" 16 | apiPassword = "pwd" 17 | 18 | # log 19 | apiLogFile = "" 20 | 21 | [app.rpc] 22 | rpcName = "wbcomic web service" 23 | rpcVersion = "1.0" 24 | 25 | rpcAddr = "localhost:50051" 26 | rpcTraceAddr = "localhost:50052" 27 | 28 | rpcLogFile = "" 29 | 30 | 31 | 32 | 33 | 34 | 35 | # 数据库配置 36 | [database.comic] 37 | master = [ 38 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s", 39 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s" 40 | ] 41 | slave = [ 42 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s", 43 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s" 44 | ] 45 | 46 | 47 | [database.page] 48 | master = [ 49 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s", 50 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s" 51 | ] 52 | slave = [ 53 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s", 54 | "root:123456@tcp(127.0.0.1:3306)/solr?charset=utf8&timeout=5s" 55 | ] 56 | 57 | 58 | 59 | 60 | 61 | [elasticsearch.comic] 62 | master = [ 63 | "http://172.16.234.163:9200", 64 | "http://172.16.234.163:9200" 65 | ] 66 | slave = [ 67 | "http://172.16.234.163:9200", 68 | "http://172.16.234.163:9200" 69 | ] 70 | 71 | 72 | [elasticsearch.author] 73 | master = [ 74 | "http://172.16.234.163:9200", 75 | "http://172.16.234.163:9200" 76 | ] 77 | slave = [ 78 | "http://172.16.234.163:9200", 79 | "http://172.16.234.163:9200" 80 | ] -------------------------------------------------------------------------------- /conf/conf_pro.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pzhen/gogrpcgin/ab959b69840b9470570435af97ad04661905405d/conf/conf_pro.toml -------------------------------------------------------------------------------- /core/elastic.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "gogrpcgin/conf" 5 | "math/rand" 6 | "gogrpcgin/utils" 7 | "encoding/json" 8 | ) 9 | 10 | type Elastic struct { 11 | Host string 12 | IndexName string 13 | TypeName string 14 | } 15 | 16 | type EsResponse struct { 17 | 18 | Error struct{ 19 | Type string `json:"type"` 20 | } 21 | 22 | Status int64 `json:"status"` 23 | 24 | Shards struct{ 25 | Failed int64 `json:"failed"` 26 | Successful int64 `json:"successful"` 27 | total int64 `json:"total"` 28 | } 29 | 30 | Hits struct{ 31 | Hits []struct{ 32 | Score float64 `json:"_score"` 33 | Source map[string]interface{} `json:"_source"` 34 | } 35 | MaxScore float64 `json:"max_score"` 36 | Total int64 `json:"total"` 37 | } 38 | } 39 | 40 | 41 | func MasterES(indexName string, typeName string) *Elastic { 42 | 43 | return &Elastic{ 44 | Host: conf.Conf.ES[indexName]["master"][rand.Intn(len(conf.Conf.ES[indexName]["master"]))], 45 | IndexName:indexName, 46 | TypeName:typeName, 47 | } 48 | } 49 | 50 | func SlaveES(indexName string, typeName string) *Elastic{ 51 | 52 | return &Elastic{ 53 | Host: conf.Conf.ES[indexName]["slave"][rand.Intn(len(conf.Conf.ES[indexName]["slave"]))], 54 | IndexName:indexName, 55 | TypeName:typeName, 56 | } 57 | } 58 | 59 | func (e *Elastic)Query(q string, v *EsResponse) { 60 | 61 | indexUrl := e.Host + "/" + e.IndexName +"/" + e.TypeName + "/_search/" 62 | 63 | r,_ := utils.HttPost(indexUrl, q) 64 | 65 | json.Unmarshal(r, v) 66 | 67 | if v.Error.Type != "" { 68 | panic("elasticsearch is error :" + v.Error.Type) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/mysql.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "math/rand" 5 | "gogrpcgin/conf" 6 | "gogrpcgin/utils" 7 | 8 | "github.com/go-xorm/xorm" 9 | _ "github.com/go-sql-driver/mysql" 10 | ) 11 | 12 | func MasterDB(dbName string) (dbResource *xorm.Engine){ 13 | 14 | if dbName == "" { 15 | dbName = "comic" 16 | } 17 | 18 | dbResource, err := xorm.NewEngine("mysql", conf.Conf.DB[dbName]["master"][rand.Intn(len(conf.Conf.DB[dbName]["master"]))]) 19 | utils.ErrToPanic(err) 20 | 21 | return dbResource 22 | } 23 | 24 | 25 | func SlaveDB(dbName string) (dbResource *xorm.Engine){ 26 | 27 | if dbName == "" { 28 | dbName = "comic" 29 | } 30 | 31 | dbResource, err := xorm.NewEngine("mysql", conf.Conf.DB[dbName]["slave"][rand.Intn(len(conf.Conf.DB[dbName]["slave"]))]) 32 | utils.ErrToPanic(err) 33 | 34 | return dbResource 35 | } -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: gogrpcgin 2 | import: 3 | - package: google.golang.org/grpc 4 | - package: github.com/golang/protobuf/protoc-gen-go 5 | - package: golang.org/x/net/context 6 | - package: golang.org/x/net/trace 7 | - package: golang.org/x/net/http2 8 | - package: golang.org/x/net/http2/hpack 9 | - package: golang.org/x/text/secure/bidirule 10 | - package: golang.org/x/text/unicode/bidi 11 | - package: golang.org/x/text/unicode/norm 12 | - package: google.golang.org/genproto/googleapis/rpc/status 13 | - package: github.com/go-sql-driver/mysql 14 | - package: github.com/go-xorm/xorm 15 | - package: github.com/go-xorm/builder 16 | - package: github.com/go-xorm/core 17 | - package: github.com/go-xorm/cmd/xorm 18 | - package: github.com/gin-gonic/gin 19 | - package: github.com/BurntSushi/toml 20 | #- package: github.com/grpc-ecosystem/go-grpc-middleware/recovery 21 | - package: github.com/ekyoung/gin-nice-recovery 22 | - package: github.com/gin-gonic/contrib 23 | - package: github.com/dgrijalva/jwt-go 24 | - package: github.com/gin-gonic/gin 25 | - package: github.com/DeanThompson/ginpprof -------------------------------------------------------------------------------- /pb/srvcomic.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: pb/srvcomic.proto 3 | 4 | /* 5 | Package pb is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | pb/srvcomic.proto 9 | 10 | It has these top-level messages: 11 | ComicFilterRequest 12 | ComicRowResponse 13 | ComicListResponse 14 | AddComicResponse 15 | */ 16 | package pb 17 | 18 | import proto "github.com/golang/protobuf/proto" 19 | import fmt "fmt" 20 | import math "math" 21 | 22 | import ( 23 | context "golang.org/x/net/context" 24 | grpc "google.golang.org/grpc" 25 | ) 26 | 27 | // Reference imports to suppress errors if they are not otherwise used. 28 | var _ = proto.Marshal 29 | var _ = fmt.Errorf 30 | var _ = math.Inf 31 | 32 | // This is a compile-time assertion to ensure that this generated file 33 | // is compatible with the proto package it is being compiled against. 34 | // A compilation error at this line likely means your copy of the 35 | // proto package needs to be updated. 36 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 37 | 38 | type ComicFilterRequest struct { 39 | ComicId int64 `protobuf:"varint,1,opt,name=comic_id,json=comicId" json:"comic_id,omitempty"` 40 | Status int64 `protobuf:"varint,2,opt,name=status" json:"status,omitempty"` 41 | RowNum int64 `protobuf:"varint,3,opt,name=row_num,json=rowNum" json:"row_num,omitempty"` 42 | PageNum int64 `protobuf:"varint,4,opt,name=page_num,json=pageNum" json:"page_num,omitempty"` 43 | } 44 | 45 | func (m *ComicFilterRequest) Reset() { *m = ComicFilterRequest{} } 46 | func (m *ComicFilterRequest) String() string { return proto.CompactTextString(m) } 47 | func (*ComicFilterRequest) ProtoMessage() {} 48 | func (*ComicFilterRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 49 | 50 | func (m *ComicFilterRequest) GetComicId() int64 { 51 | if m != nil { 52 | return m.ComicId 53 | } 54 | return 0 55 | } 56 | 57 | func (m *ComicFilterRequest) GetStatus() int64 { 58 | if m != nil { 59 | return m.Status 60 | } 61 | return 0 62 | } 63 | 64 | func (m *ComicFilterRequest) GetRowNum() int64 { 65 | if m != nil { 66 | return m.RowNum 67 | } 68 | return 0 69 | } 70 | 71 | func (m *ComicFilterRequest) GetPageNum() int64 { 72 | if m != nil { 73 | return m.PageNum 74 | } 75 | return 0 76 | } 77 | 78 | type ComicRowResponse struct { 79 | ComicId int64 `protobuf:"varint,1,opt,name=comic_id,json=comicId" json:"comic_id,omitempty"` 80 | ComicName string `protobuf:"bytes,2,opt,name=comic_name,json=comicName" json:"comic_name,omitempty"` 81 | CreateTime int64 `protobuf:"varint,3,opt,name=create_time,json=createTime" json:"create_time,omitempty"` 82 | Status int32 `protobuf:"varint,4,opt,name=status" json:"status,omitempty"` 83 | } 84 | 85 | func (m *ComicRowResponse) Reset() { *m = ComicRowResponse{} } 86 | func (m *ComicRowResponse) String() string { return proto.CompactTextString(m) } 87 | func (*ComicRowResponse) ProtoMessage() {} 88 | func (*ComicRowResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 89 | 90 | func (m *ComicRowResponse) GetComicId() int64 { 91 | if m != nil { 92 | return m.ComicId 93 | } 94 | return 0 95 | } 96 | 97 | func (m *ComicRowResponse) GetComicName() string { 98 | if m != nil { 99 | return m.ComicName 100 | } 101 | return "" 102 | } 103 | 104 | func (m *ComicRowResponse) GetCreateTime() int64 { 105 | if m != nil { 106 | return m.CreateTime 107 | } 108 | return 0 109 | } 110 | 111 | func (m *ComicRowResponse) GetStatus() int32 { 112 | if m != nil { 113 | return m.Status 114 | } 115 | return 0 116 | } 117 | 118 | type ComicListResponse struct { 119 | RowsTotal int64 `protobuf:"varint,1,opt,name=rows_total,json=rowsTotal" json:"rows_total,omitempty"` 120 | PageNum int64 `protobuf:"varint,2,opt,name=page_num,json=pageNum" json:"page_num,omitempty"` 121 | RowNum int64 `protobuf:"varint,3,opt,name=row_num,json=rowNum" json:"row_num,omitempty"` 122 | PageTotal int64 `protobuf:"varint,4,opt,name=page_total,json=pageTotal" json:"page_total,omitempty"` 123 | Data []*ComicRowResponse `protobuf:"bytes,5,rep,name=data" json:"data,omitempty"` 124 | } 125 | 126 | func (m *ComicListResponse) Reset() { *m = ComicListResponse{} } 127 | func (m *ComicListResponse) String() string { return proto.CompactTextString(m) } 128 | func (*ComicListResponse) ProtoMessage() {} 129 | func (*ComicListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 130 | 131 | func (m *ComicListResponse) GetRowsTotal() int64 { 132 | if m != nil { 133 | return m.RowsTotal 134 | } 135 | return 0 136 | } 137 | 138 | func (m *ComicListResponse) GetPageNum() int64 { 139 | if m != nil { 140 | return m.PageNum 141 | } 142 | return 0 143 | } 144 | 145 | func (m *ComicListResponse) GetRowNum() int64 { 146 | if m != nil { 147 | return m.RowNum 148 | } 149 | return 0 150 | } 151 | 152 | func (m *ComicListResponse) GetPageTotal() int64 { 153 | if m != nil { 154 | return m.PageTotal 155 | } 156 | return 0 157 | } 158 | 159 | func (m *ComicListResponse) GetData() []*ComicRowResponse { 160 | if m != nil { 161 | return m.Data 162 | } 163 | return nil 164 | } 165 | 166 | type AddComicResponse struct { 167 | ComicId int64 `protobuf:"varint,1,opt,name=comic_id,json=comicId" json:"comic_id,omitempty"` 168 | Success bool `protobuf:"varint,2,opt,name=success" json:"success,omitempty"` 169 | } 170 | 171 | func (m *AddComicResponse) Reset() { *m = AddComicResponse{} } 172 | func (m *AddComicResponse) String() string { return proto.CompactTextString(m) } 173 | func (*AddComicResponse) ProtoMessage() {} 174 | func (*AddComicResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 175 | 176 | func (m *AddComicResponse) GetComicId() int64 { 177 | if m != nil { 178 | return m.ComicId 179 | } 180 | return 0 181 | } 182 | 183 | func (m *AddComicResponse) GetSuccess() bool { 184 | if m != nil { 185 | return m.Success 186 | } 187 | return false 188 | } 189 | 190 | func init() { 191 | proto.RegisterType((*ComicFilterRequest)(nil), "pb.ComicFilterRequest") 192 | proto.RegisterType((*ComicRowResponse)(nil), "pb.ComicRowResponse") 193 | proto.RegisterType((*ComicListResponse)(nil), "pb.ComicListResponse") 194 | proto.RegisterType((*AddComicResponse)(nil), "pb.AddComicResponse") 195 | } 196 | 197 | // Reference imports to suppress errors if they are not otherwise used. 198 | var _ context.Context 199 | var _ grpc.ClientConn 200 | 201 | // This is a compile-time assertion to ensure that this generated file 202 | // is compatible with the grpc package it is being compiled against. 203 | const _ = grpc.SupportPackageIsVersion4 204 | 205 | // Client API for ComicService service 206 | 207 | type ComicServiceClient interface { 208 | GetComicRowById(ctx context.Context, in *ComicFilterRequest, opts ...grpc.CallOption) (*ComicRowResponse, error) 209 | AddComic(ctx context.Context, in *ComicRowResponse, opts ...grpc.CallOption) (*AddComicResponse, error) 210 | GetComicListByPage(ctx context.Context, in *ComicFilterRequest, opts ...grpc.CallOption) (*ComicListResponse, error) 211 | } 212 | 213 | type comicServiceClient struct { 214 | cc *grpc.ClientConn 215 | } 216 | 217 | func NewComicServiceClient(cc *grpc.ClientConn) ComicServiceClient { 218 | return &comicServiceClient{cc} 219 | } 220 | 221 | func (c *comicServiceClient) GetComicRowById(ctx context.Context, in *ComicFilterRequest, opts ...grpc.CallOption) (*ComicRowResponse, error) { 222 | out := new(ComicRowResponse) 223 | err := grpc.Invoke(ctx, "/pb.ComicService/GetComicRowById", in, out, c.cc, opts...) 224 | if err != nil { 225 | return nil, err 226 | } 227 | return out, nil 228 | } 229 | 230 | func (c *comicServiceClient) AddComic(ctx context.Context, in *ComicRowResponse, opts ...grpc.CallOption) (*AddComicResponse, error) { 231 | out := new(AddComicResponse) 232 | err := grpc.Invoke(ctx, "/pb.ComicService/AddComic", in, out, c.cc, opts...) 233 | if err != nil { 234 | return nil, err 235 | } 236 | return out, nil 237 | } 238 | 239 | func (c *comicServiceClient) GetComicListByPage(ctx context.Context, in *ComicFilterRequest, opts ...grpc.CallOption) (*ComicListResponse, error) { 240 | out := new(ComicListResponse) 241 | err := grpc.Invoke(ctx, "/pb.ComicService/GetComicListByPage", in, out, c.cc, opts...) 242 | if err != nil { 243 | return nil, err 244 | } 245 | return out, nil 246 | } 247 | 248 | // Server API for ComicService service 249 | 250 | type ComicServiceServer interface { 251 | GetComicRowById(context.Context, *ComicFilterRequest) (*ComicRowResponse, error) 252 | AddComic(context.Context, *ComicRowResponse) (*AddComicResponse, error) 253 | GetComicListByPage(context.Context, *ComicFilterRequest) (*ComicListResponse, error) 254 | } 255 | 256 | func RegisterComicServiceServer(s *grpc.Server, srv ComicServiceServer) { 257 | s.RegisterService(&_ComicService_serviceDesc, srv) 258 | } 259 | 260 | func _ComicService_GetComicRowById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 261 | in := new(ComicFilterRequest) 262 | if err := dec(in); err != nil { 263 | return nil, err 264 | } 265 | if interceptor == nil { 266 | return srv.(ComicServiceServer).GetComicRowById(ctx, in) 267 | } 268 | info := &grpc.UnaryServerInfo{ 269 | Server: srv, 270 | FullMethod: "/pb.ComicService/GetComicRowById", 271 | } 272 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 273 | return srv.(ComicServiceServer).GetComicRowById(ctx, req.(*ComicFilterRequest)) 274 | } 275 | return interceptor(ctx, in, info, handler) 276 | } 277 | 278 | func _ComicService_AddComic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 279 | in := new(ComicRowResponse) 280 | if err := dec(in); err != nil { 281 | return nil, err 282 | } 283 | if interceptor == nil { 284 | return srv.(ComicServiceServer).AddComic(ctx, in) 285 | } 286 | info := &grpc.UnaryServerInfo{ 287 | Server: srv, 288 | FullMethod: "/pb.ComicService/AddComic", 289 | } 290 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 291 | return srv.(ComicServiceServer).AddComic(ctx, req.(*ComicRowResponse)) 292 | } 293 | return interceptor(ctx, in, info, handler) 294 | } 295 | 296 | func _ComicService_GetComicListByPage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 297 | in := new(ComicFilterRequest) 298 | if err := dec(in); err != nil { 299 | return nil, err 300 | } 301 | if interceptor == nil { 302 | return srv.(ComicServiceServer).GetComicListByPage(ctx, in) 303 | } 304 | info := &grpc.UnaryServerInfo{ 305 | Server: srv, 306 | FullMethod: "/pb.ComicService/GetComicListByPage", 307 | } 308 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 309 | return srv.(ComicServiceServer).GetComicListByPage(ctx, req.(*ComicFilterRequest)) 310 | } 311 | return interceptor(ctx, in, info, handler) 312 | } 313 | 314 | var _ComicService_serviceDesc = grpc.ServiceDesc{ 315 | ServiceName: "pb.ComicService", 316 | HandlerType: (*ComicServiceServer)(nil), 317 | Methods: []grpc.MethodDesc{ 318 | { 319 | MethodName: "GetComicRowById", 320 | Handler: _ComicService_GetComicRowById_Handler, 321 | }, 322 | { 323 | MethodName: "AddComic", 324 | Handler: _ComicService_AddComic_Handler, 325 | }, 326 | { 327 | MethodName: "GetComicListByPage", 328 | Handler: _ComicService_GetComicListByPage_Handler, 329 | }, 330 | }, 331 | Streams: []grpc.StreamDesc{}, 332 | Metadata: "pb/srvcomic.proto", 333 | } 334 | 335 | func init() { proto.RegisterFile("pb/srvcomic.proto", fileDescriptor0) } 336 | 337 | var fileDescriptor0 = []byte{ 338 | // 381 bytes of a gzipped FileDescriptorProto 339 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdd, 0x6a, 0xea, 0x40, 340 | 0x10, 0xc7, 0x8d, 0xdf, 0x19, 0x0f, 0x1c, 0x5d, 0xce, 0x47, 0xce, 0x01, 0x39, 0x92, 0x2b, 0xaf, 341 | 0x3c, 0x60, 0x6f, 0x7a, 0xab, 0xa5, 0x95, 0x42, 0x91, 0x92, 0x7a, 0x1f, 0x36, 0xc9, 0x20, 0x01, 342 | 0xd7, 0x4d, 0x77, 0x37, 0x06, 0xe9, 0x75, 0x9f, 0xa7, 0xef, 0xd2, 0x27, 0x2a, 0xbb, 0x6b, 0x44, 343 | 0x45, 0x4b, 0x2f, 0xe7, 0x3f, 0xb3, 0xf3, 0xff, 0xcd, 0xcc, 0x42, 0x2f, 0x8b, 0xfe, 0x4b, 0xb1, 344 | 0x89, 0x39, 0x4b, 0xe3, 0x51, 0x26, 0xb8, 0xe2, 0xa4, 0x9a, 0x45, 0xfe, 0x0b, 0x90, 0x1b, 0x2d, 345 | 0xdd, 0xa5, 0x2b, 0x85, 0x22, 0xc0, 0xe7, 0x1c, 0xa5, 0x22, 0x7f, 0xa0, 0x6d, 0x0a, 0xc3, 0x34, 346 | 0xf1, 0x9c, 0x81, 0x33, 0xac, 0x05, 0x2d, 0x13, 0xdf, 0x27, 0xe4, 0x17, 0x34, 0xa5, 0xa2, 0x2a, 347 | 0x97, 0x5e, 0xd5, 0x24, 0x76, 0x11, 0xf9, 0x0d, 0x2d, 0xc1, 0x8b, 0x70, 0x9d, 0x33, 0xaf, 0x66, 348 | 0x13, 0x82, 0x17, 0xf3, 0x9c, 0xe9, 0x5e, 0x19, 0x5d, 0xa2, 0xc9, 0xd4, 0x6d, 0x2f, 0x1d, 0xcf, 349 | 0x73, 0xe6, 0xbf, 0x3a, 0xd0, 0x35, 0xee, 0x01, 0x2f, 0x02, 0x94, 0x19, 0x5f, 0x4b, 0xfc, 0xcc, 350 | 0xbb, 0x0f, 0x60, 0x53, 0x6b, 0xca, 0xd0, 0xf8, 0xbb, 0x81, 0x6b, 0x94, 0x39, 0x65, 0x48, 0xfe, 351 | 0x41, 0x27, 0x16, 0x48, 0x15, 0x86, 0x2a, 0x65, 0xb8, 0xc3, 0x00, 0x2b, 0x2d, 0x52, 0x86, 0x07, 352 | 0xec, 0x1a, 0xa4, 0x51, 0xb2, 0xfb, 0x6f, 0x0e, 0xf4, 0x0c, 0xc7, 0x43, 0x2a, 0xd5, 0x1e, 0xa4, 353 | 0x0f, 0x20, 0x78, 0x21, 0x43, 0xc5, 0x15, 0x5d, 0xed, 0x50, 0x5c, 0xad, 0x2c, 0xb4, 0x70, 0x34, 354 | 0x57, 0xf5, 0x68, 0xae, 0xcb, 0xbb, 0xe8, 0x03, 0x98, 0x37, 0xb6, 0xa5, 0xdd, 0x86, 0xab, 0x15, 355 | 0xdb, 0x72, 0x08, 0xf5, 0x84, 0x2a, 0xea, 0x35, 0x06, 0xb5, 0x61, 0x67, 0xfc, 0x63, 0x94, 0x45, 356 | 0xa3, 0xd3, 0xf5, 0x04, 0xa6, 0xc2, 0x9f, 0x41, 0x77, 0x92, 0x24, 0x36, 0xf9, 0x85, 0xc5, 0x79, 357 | 0xd0, 0x92, 0x79, 0x1c, 0xa3, 0xb4, 0x57, 0x6b, 0x07, 0x65, 0x38, 0x7e, 0x77, 0xe0, 0x9b, 0x69, 358 | 0xf3, 0x84, 0x62, 0x93, 0xc6, 0x48, 0x26, 0xf0, 0x7d, 0x86, 0xaa, 0xb4, 0x9d, 0x6e, 0xf5, 0xc9, 359 | 0xf7, 0x20, 0x47, 0xbf, 0xe4, 0xef, 0x59, 0x40, 0xbf, 0x42, 0xae, 0xa1, 0x5d, 0xc2, 0x91, 0xb3, 360 | 0x35, 0xf6, 0xe5, 0xe9, 0x00, 0x7e, 0x85, 0xdc, 0x02, 0x29, 0xcd, 0xf5, 0x29, 0xa6, 0xdb, 0x47, 361 | 0xba, 0xc4, 0x8b, 0xfe, 0x3f, 0xf7, 0xfa, 0xe1, 0xdd, 0xfc, 0x4a, 0xd4, 0x34, 0xff, 0xfb, 0xea, 362 | 0x23, 0x00, 0x00, 0xff, 0xff, 0xde, 0x06, 0x88, 0xac, 0xf4, 0x02, 0x00, 0x00, 363 | } 364 | -------------------------------------------------------------------------------- /pb/srvcomic.proto: -------------------------------------------------------------------------------- 1 | //protoc -I ./ pb/srvcomic.proto --go_out=plugins=grpc:./ 2 | 3 | syntax = "proto3"; 4 | package pb; 5 | 6 | service ComicService { 7 | rpc GetComicRowById (ComicFilterRequest) returns (ComicRowResponse) {}; 8 | rpc AddComic(ComicRowResponse) returns (AddComicResponse) {}; 9 | rpc GetComicListByPage (ComicFilterRequest) returns (ComicListResponse) {}; 10 | } 11 | 12 | message ComicFilterRequest { 13 | int64 comic_id = 1; 14 | int64 status = 2; 15 | int64 row_num = 3; 16 | int64 page_num = 4; 17 | } 18 | 19 | message ComicRowResponse{ 20 | int64 comic_id = 1; 21 | string comic_name = 2; 22 | int64 create_time = 3; 23 | int32 status = 4; 24 | } 25 | 26 | message ComicListResponse{ 27 | int64 rows_total = 1; 28 | int64 page_num = 2; 29 | int64 row_num = 3; 30 | int64 page_total = 4; 31 | repeated ComicRowResponse data = 5; 32 | } 33 | 34 | message AddComicResponse{ 35 | int64 comic_id = 1; 36 | bool success = 2; 37 | } -------------------------------------------------------------------------------- /rpc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import( 4 | "net" 5 | "errors" 6 | "runtime" 7 | "context" 8 | "net/http" 9 | "time" 10 | 11 | "gogrpcgin/conf" 12 | "gogrpcgin/utils" 13 | "gogrpcgin/service" 14 | 15 | "google.golang.org/grpc/reflection" 16 | "google.golang.org/grpc" 17 | "golang.org/x/net/trace" 18 | ) 19 | 20 | // dev/pro 21 | const Env = "dev" 22 | 23 | // rpc run 24 | func main() { 25 | 26 | // init config 27 | conf.InitConfig(Env,"rpc") 28 | 29 | //new server engine 30 | server := newServer() 31 | 32 | // register service 33 | service.ServiceReg(server) 34 | 35 | // run 36 | serviceRun(server) 37 | } 38 | 39 | // service run 40 | func serviceRun(server *grpc.Server) { 41 | 42 | address := conf.Conf.App.Rpc.RpcAddr 43 | lis, err := net.Listen("tcp", address) 44 | if err != nil { 45 | utils.LogFatalfError(err) 46 | } 47 | utils.LogPrint("RPC server listen on %s\n", address) 48 | 49 | // open trace 50 | startTrace() 51 | 52 | // 开启服务 53 | reflection.Register(server) 54 | if err := server.Serve(lis); err != nil { 55 | utils.LogFatalfError(err) 56 | } 57 | } 58 | 59 | // open trace 60 | // like: http://localhost:50052/debug/requests 61 | // like: http://localhost:50052/debug/events 62 | func startTrace() { 63 | 64 | grpc.EnableTracing = true 65 | 66 | trace.AuthRequest = func(req *http.Request) (any, sensitive bool) { 67 | return true, true 68 | } 69 | 70 | var traceAddr = conf.Conf.App.Rpc.RpcTraceAddr 71 | 72 | utils.LogPrint("Trace listen on %s\n",traceAddr) 73 | go http.ListenAndServe(traceAddr, nil) 74 | } 75 | 76 | 77 | // new server engine 78 | func newServer() *grpc.Server{ 79 | 80 | return grpc.NewServer(grpc.UnaryInterceptor(composeUnaryServerInterceptors(serverRecoveryInterceptorHandle, 81 | serverDurationInterceptorHandle))) 82 | } 83 | 84 | // server recovery interceptor handle 85 | func serverRecoveryInterceptorHandle(ctx context.Context,req interface{},info *grpc.UnaryServerInfo, 86 | handler grpc.UnaryHandler)(interface{}, error) { 87 | 88 | defer func() (err error){ 89 | if r := recover(); r != nil { 90 | str, ok := r.(string) 91 | 92 | if ok { 93 | err = errors.New(str) 94 | } else { 95 | err = errors.New("panic!!!") 96 | } 97 | 98 | //打印堆栈 99 | stack := make([]byte, 1024*8) 100 | stack = stack[:runtime.Stack(stack, false)] 101 | 102 | utils.LogPrint("[Recovery] panic recovered:\n\n%s\n\n%s" , r,stack) 103 | } 104 | 105 | return 106 | }() 107 | 108 | return handler(ctx, req) 109 | } 110 | 111 | // server duration interceptor handle 112 | func serverDurationInterceptorHandle(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 113 | handler grpc.UnaryHandler) (interface{}, error) { 114 | 115 | start := time.Now() 116 | resp, err := handler(ctx, req) 117 | 118 | utils.LogPrint("invoke server method=%s duration=%s error=%v", info.FullMethod, time.Since(start), err) 119 | 120 | return resp, err 121 | } 122 | 123 | // multi interceptors 124 | func composeUnaryServerInterceptors(interceptor, next grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor { 125 | return func( 126 | ctx context.Context, 127 | req interface{}, 128 | info *grpc.UnaryServerInfo, 129 | handler grpc.UnaryHandler) (interface{}, error) { 130 | 131 | return interceptor(ctx, req, info, 132 | func(nextCtx context.Context, nextReq interface{}) (interface{}, error) { 133 | 134 | return next(nextCtx, nextReq, info, handler) 135 | }) 136 | } 137 | } 138 | 139 | -------------------------------------------------------------------------------- /service/register.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "google.golang.org/grpc" 5 | "gogrpcgin/pb" 6 | "gogrpcgin/service/srv-comic" 7 | ) 8 | 9 | func ServiceReg(server *grpc.Server) { 10 | 11 | pb.RegisterComicServiceServer(server,&srv_comic.ComicInfo{}) 12 | 13 | } 14 | -------------------------------------------------------------------------------- /service/srv-comic/srv-comic.go: -------------------------------------------------------------------------------- 1 | package srv_comic 2 | 3 | import( 4 | "context" 5 | 6 | "gogrpcgin/pb" 7 | "gogrpcgin/core" 8 | ) 9 | 10 | type ComicInfo struct { 11 | cc pb.ComicServiceClient `xorm:"-"` 12 | ComicId int64 `xorm:"not null pk autoincr INT(11)"` 13 | ComicName string `xorm:"not null VARCHAR(50)"` 14 | Status int32 `xorm:"not null default 1 TINYINT(3)"` 15 | CreateTime int64 `xorm:"not null index INT(10)"` 16 | } 17 | 18 | 19 | func (c *ComicInfo) GetComicRowById (ctx context.Context, in *pb.ComicFilterRequest) (*pb.ComicRowResponse, error){ 20 | 21 | comicRow := new(ComicInfo) 22 | _, err := core.MasterDB("comic").Id(in.ComicId).Get(comicRow) 23 | 24 | return &pb.ComicRowResponse{ 25 | ComicId:comicRow.ComicId, 26 | ComicName:comicRow.ComicName, 27 | Status:comicRow.Status, 28 | CreateTime:comicRow.CreateTime, 29 | },err 30 | 31 | } 32 | 33 | func (c *ComicInfo) GetComicListByPage (ctx context.Context, in *pb.ComicFilterRequest) (comicListResponse *pb.ComicListResponse, err error){ 34 | 35 | limit := int(in.RowNum) 36 | start := int((in.PageNum - 1) * in.RowNum) 37 | 38 | comicList := make([]ComicInfo, 0) 39 | err = core.MasterDB("comic").Where("status = ?", 1).Limit(limit, start).Find(&comicList) 40 | 41 | // time.Sleep(3*time.Second) 42 | ////panic("aaaaa3333") 43 | comicListResponse = new(pb.ComicListResponse) 44 | 45 | for _,v := range comicList{ 46 | comicListResponse.Data = append(comicListResponse.Data, &pb.ComicRowResponse{ 47 | v.ComicId, 48 | v.ComicName, 49 | v.CreateTime, 50 | v.Status, 51 | }) 52 | } 53 | 54 | return comicListResponse,err 55 | } 56 | 57 | func (c *ComicInfo)AddComic(ctx context.Context, in *pb.ComicRowResponse) (*pb.AddComicResponse, error) { 58 | 59 | comicRow := new(ComicInfo) 60 | comicRow.ComicName = in.ComicName 61 | comicRow.Status = in.Status 62 | comicRow.CreateTime = in.CreateTime 63 | 64 | _, err := core.MasterDB("comic").Insert(comicRow) 65 | 66 | return &pb.AddComicResponse{ 67 | ComicId:comicRow.ComicId, 68 | },err 69 | } -------------------------------------------------------------------------------- /utils/utils_file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "os" 4 | 5 | // FileExists reports whether the named file or directory exists. 6 | func FileExists(name string) bool { 7 | if _, err := os.Stat(name); err != nil { 8 | if os.IsNotExist(err) { 9 | return false 10 | } 11 | } 12 | return true 13 | } 14 | -------------------------------------------------------------------------------- /utils/utils_http.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strings" 5 | "net/http" 6 | "io/ioutil" 7 | ) 8 | 9 | func HttPost(url string, jsonStr string) (r []byte, err error){ 10 | 11 | payload := strings.NewReader(jsonStr) 12 | req, _ := http.NewRequest("POST", url, payload) 13 | 14 | req.Header.Add("authorization", "") 15 | req.Header.Add("content-type", "application/json") 16 | req.Header.Add("cache-control", "no-cache") 17 | 18 | res, err := http.DefaultClient.Do(req) 19 | 20 | defer res.Body.Close() 21 | 22 | if err != nil{ 23 | return nil ,err 24 | } 25 | 26 | body, err := ioutil.ReadAll(res.Body) 27 | 28 | if err != nil{ 29 | return nil ,err 30 | } 31 | 32 | return body,nil 33 | } 34 | -------------------------------------------------------------------------------- /utils/utils_log.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | func init() { 8 | 9 | log.SetFlags(0) 10 | } 11 | 12 | // 日志 打印 13 | func LogPrint(format string, values ...interface{}) { 14 | 15 | log.Printf("[RPC-debug] "+format, values...) 16 | } 17 | 18 | // 错误 打印 19 | func LogPrintError(err error) { 20 | 21 | if err != nil { 22 | LogPrint("[ERROR] %v\n", err) 23 | } 24 | } 25 | 26 | // 错误 打印&&退出 27 | func LogFatalfError(err error) { 28 | 29 | if err != nil { 30 | log.Fatalf("[ERROR] %v\n", err) 31 | } 32 | } 33 | 34 | // 错误 转异常抛出 35 | func ErrToPanic(e error) { 36 | if e != nil { 37 | panic(e) 38 | } 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /utils/utils_map.go: -------------------------------------------------------------------------------- 1 | // m := utils.NewBeeMap() 2 | // m.Set("nihao","hahaha") 3 | package utils 4 | 5 | import ( 6 | "sync" 7 | ) 8 | 9 | // SafeMap is a map with lock 10 | type SafeMap struct { 11 | lock *sync.RWMutex 12 | bm map[interface{}]interface{} 13 | } 14 | 15 | // NewSafeMap return new safemap 16 | func NewSafeMap() *SafeMap { 17 | return &SafeMap{ 18 | lock: new(sync.RWMutex), 19 | bm: make(map[interface{}]interface{}), 20 | } 21 | } 22 | 23 | // Get from maps return the k's value 24 | func (m *SafeMap) Get(k interface{}) interface{} { 25 | m.lock.RLock() 26 | defer m.lock.RUnlock() 27 | if val, ok := m.bm[k]; ok { 28 | return val 29 | } 30 | return nil 31 | } 32 | 33 | // Set Maps the given key and value. Returns false 34 | // if the key is already in the map and changes nothing. 35 | func (m *SafeMap) Set(k interface{}, v interface{}) bool { 36 | m.lock.Lock() 37 | defer m.lock.Unlock() 38 | if val, ok := m.bm[k]; !ok { 39 | m.bm[k] = v 40 | } else if val != v { 41 | m.bm[k] = v 42 | } else { 43 | return false 44 | } 45 | return true 46 | } 47 | 48 | // Check Returns true if k is exist in the map. 49 | func (m *SafeMap) Check(k interface{}) bool { 50 | m.lock.RLock() 51 | defer m.lock.RUnlock() 52 | _, ok := m.bm[k] 53 | return ok 54 | } 55 | 56 | // Delete the given key and value. 57 | func (m *SafeMap) Delete(k interface{}) { 58 | m.lock.Lock() 59 | defer m.lock.Unlock() 60 | delete(m.bm, k) 61 | } 62 | 63 | // Items returns all items in safemap. 64 | func (m *SafeMap) Items() map[interface{}]interface{} { 65 | m.lock.RLock() 66 | defer m.lock.RUnlock() 67 | r := make(map[interface{}]interface{}) 68 | for k, v := range m.bm { 69 | r[k] = v 70 | } 71 | return r 72 | } 73 | 74 | // Count returns the number of items within the map. 75 | func (m *SafeMap) Count() int { 76 | m.lock.RLock() 77 | defer m.lock.RUnlock() 78 | return len(m.bm) 79 | } 80 | -------------------------------------------------------------------------------- /utils/utils_string.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import( 4 | "strings" 5 | ) 6 | 7 | //截取字符串 8 | func Substr(s string, pos, length int) string { 9 | runes := []rune(s) 10 | l := pos + length 11 | if l > len(runes) { 12 | l = len(runes) 13 | } 14 | return string(runes[pos:l]) 15 | } 16 | 17 | func Utf8Index(str, substr string) int { 18 | asciiPos := strings.Index(str, substr) 19 | if asciiPos == -1 || asciiPos == 0 { 20 | return asciiPos 21 | } 22 | pos := 0 23 | totalSize := 0 24 | reader := strings.NewReader(str) 25 | for _, size, err := reader.ReadRune(); err == nil; _, size, err = reader.ReadRune() { 26 | totalSize += size 27 | pos++ 28 | // 匹配到 29 | if totalSize == asciiPos { 30 | return pos 31 | } 32 | } 33 | return pos 34 | } --------------------------------------------------------------------------------