Mix Go is a complete system for rapid development based on Go, similar to the front-end Vue CLI, providing:
- through
mix-go/mixcli
Implementation of interactive project scaffolding:- Can be generated
cli
.api
.web
.grpc
Multiple project codes - The generated code is out of the box
- Optional
.env
Environment configuration - Optional
.yml
..json
..toml
Equal independent configuration - Optional use
gorm
.xorm
The database of - Optional use
logrus
.zap
The logging library
- Can be generated
- through
mix-go/xcli
Implementation of command line prototype development. - Based on the
mix-go/xdi
DI, IoC container.
Github | Gitee
- github.com/mix-go/mix
- gitee.com/mix-go/mix
Quick start
The installation
go get github.com/mix-go/mixcli
Copy the code
Create a project
$mixcli new hello Use the arrow keys to navigate: ↓ ↑ → ←? Select Project type: ▸ CLI API Web (contains the websocket) gRPCCopy the code
Technical communication
Zhihu: www.zhihu.com/people/onan… Weibo: weibo.com/onanying Official QQ group: 284806582, 825122875, knock password: GOer
Write a CLI program
First we create a project skeleton using the mixcli command:
$mixcli new hello Use the arrow keys to navigate: ↓ ↑ → ←? Select Project type: ▸ CLI API Web (contains the websocket) gRPCCopy the code
Generate skeleton directory structure as follows:
. ├ ─ ─ the README. Md ├ ─ ─ bin ├ ─ ─ commands ├ ─ ─ the conf ├ ─ ─ configor ├ ─ ─ di ├ ─ ─ dotenv ├ ─ ─. Mod ├ ─ ─. Sum ├ ─ ─ logs └ ─ ─ main. GoCopy the code
Mian. Go file:
xcli.AddCommand
Method passed incommands.Commands
Defines all commands
package main
import (
"github.com/mix-go/cli-skeleton/commands"
_ "github.com/mix-go/cli-skeleton/configor"
_ "github.com/mix-go/cli-skeleton/di"
_ "github.com/mix-go/cli-skeleton/dotenv"
"github.com/mix-go/dotenv"
"github.com/mix-go/xcli"
)
func main(a) {
xcli.SetName("app").
SetVersion("0.0.0 - alpha").
SetDebug(dotenv.Getenv("APP_DEBUG").Bool(false)) xcli.AddCommand(commands.Commands...) .Run() }Copy the code
Commands/main. Go file:
We can customize commands here to see more
RunI
Defines thehello
The command execution interface can also be usedRun
Set an anonymous function
package commands
import (
"github.com/mix-go/xcli"
)
var Commands = []*xcli.Command{
{
Name: "hello",
Short: "\tEcho demo",
Options: []*xcli.Option{
{
Names: []string{"n"."name"},
Usage: "Your name",
},
{
Names: []string{"say"},
Usage: "\tSay ...",
},
},
RunI: &HelloCommand{},
},
}
Copy the code
Commands/hello. Go file:
The business code is written in the main method of the HelloCommand structure
- Can be used in code
flag
Gets command line arguments,To view more
package commands
import (
"fmt"
"github.com/mix-go/xcli/flag"
)
type HelloCommand struct{}func (t *HelloCommand) Main(a) {
name := flag.Match("n"."name").String("OpenMix")
say := flag.Match("say").String("Hello, World!")
fmt.Printf("%s: %s\n", name, say)
}
Copy the code
Next we compile the above program:
- linux & macOS
go build -o bin/go_build_main_go main.go
Copy the code
- win
go build -o bin/go_build_main_go.exe main.go
Copy the code
To view the help information about all commands:
$ cd bin $ ./go_build_main_go Usage: ./go_build_main_go [OPTIONS] COMMAND [opt...] Global Options: -h, --help Print usage -v, --version Print version information Commands: hello Echo demo Run './go_build_main_go COMMAND --help' for more information on a command. Developed with Mix Go framework. (openmix.org/mix-go)Copy the code
See help for the hello command written above:
$ ./go_build_main_go hello --help Usage: ./go_build_main_go hello [opt...] Command Options: -n, --name Your name --say Say ... Developed with Mix Go framework. (openmix.org/mix-go)Copy the code
Execute the hello command with two arguments:
$ ./go_build_main_go hello --name=liujian --say=hello
liujian: hello
Copy the code
Write a Worker Pool queue to consume
Queue consumption is the most commonly used asynchronous processing model in high-concurrency systems. Usually, we write a CLI command line program to execute queue consumption of Redis, RabbitMQ and other MQ in the background, and then implement the processing results into mysql and other databases. Because the standardization of such requirements is relatively easy, So we developed the MiX-Go/XWP library to handle these requirements, and basically most of the asynchronous processing class requirements are available.
Create commands/ workerPool. go file
workerpool.NewDispatcher(jobQueue, 15, NewWorker)
A scheduler is createdNewWorker
Responsible for initializing the work coroutine that executes the task- The mission data will be in
worker.Do
Method, we just need to write our business logic to that method - When the program receives the process exit signal, the scheduler can smoothly control all workers to exit the schedule after completing all tasks in the queue to ensure data integrity
package commands
import (
"context"
"fmt"
"github.com/mix-go/cli-skeleton/di"
"github.com/mix-go/xwp"
"os"
"os/signal"
"strings"
"syscall"
"time"
)
type worker struct {
xwp.WorkerTrait
}
func (t *worker) Do(data interface{}) {
defer func(a) {
if err := recover(a); err ! =nil {
logger := di.Logrus()
logger.Error(err)
}
}()
// Perform business processing
// ...
// Drop the result to the database
// ...
}
func NewWorker(a) xwp.Worker {
return &worker{}
}
type WorkerPoolDaemonCommand struct{}func (t *WorkerPoolDaemonCommand) Main(a) {
redis := globals.Redis()
jobQueue := make(chan interface{}, 50)
d := xwp.NewDispatcher(jobQueue, 15, NewWorker)
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
go func(a) {
<-ch
d.Stop()
}()
go func(a) {
for {
res, err := redis.BRPop(context.Background(), 3*time.Second, "foo").Result()
iferr ! =nil {
if strings.Contains(err.Error(), "redis: nil") {
continue
}
fmt.Println(fmt.Sprintf("Redis Error: %s", err))
d.Stop();
return
}
// the last key of the brPop command is the value
jobQueue <- res[1]
}
}()
d.Run() // Block code until all tasks complete and all Worker stops
}
Copy the code
The next step is to register the command with the CLI through xcli.AddCommand.
Write an API service
First we create a project skeleton using the mixcli command:
$mixcli new hello Use the arrow keys to navigate: ↓ ↑ → ←? Select Project Type: CLI ▸ API Web (contains the websocket) gRPCCopy the code
Generate skeleton directory structure as follows:
. ├ ─ ─ the README. Md ├ ─ ─ bin ├ ─ ─ commands ├ ─ ─ the conf ├ ─ ─ configor ├ ─ ─ controllers ├ ─ ─ di ├ ─ ─ dotenv ├ ─ ─. Mod ├ ─ ─. Sum ├ ─ ─ ├── bass Exercises ── exercisesCopy the code
Mian. Go file:
xcli.AddCommand
Method passed incommands.Commands
Defines all commands
package main
import (
"github.com/mix-go/api-skeleton/commands"
_ "github.com/mix-go/api-skeleton/configor"
_ "github.com/mix-go/api-skeleton/di"
_ "github.com/mix-go/api-skeleton/dotenv"
"github.com/mix-go/dotenv"
"github.com/mix-go/xcli"
)
func main(a) {
xcli.SetName("app").
SetVersion("0.0.0 - alpha").
SetDebug(dotenv.Getenv("APP_DEBUG").Bool(false)) xcli.AddCommand(commands.Commands...) .Run() }Copy the code
Commands/main. Go file:
We can customize commands here to see more
RunI
Specifies the interface for executing the commandRun
Set an anonymous function
package commands
import (
"github.com/mix-go/xcli"
)
var Commands = []*xcli.Command{
{
Name: "api",
Short: "\tStart the api server",
Options: []*xcli.Option{
{
Names: []string{"a"."addr"},
Usage: "\tListen to the specified address",
},
{
Names: []string{"d"."daemon"},
Usage: "\tRun in the background",
},
},
RunI: &APICommand{},
},
}
Copy the code
Commands/API. Go file:
The business code is written in the main method of the APICommand structure, and the generated code already contains:
- Listening signal stops service
- Logs are printed based on the mode
- Optional background daemon execution
Basically, it can be used online without modification
package commands
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/mix-go/api-skeleton/di"
"github.com/mix-go/api-skeleton/routes"
"github.com/mix-go/dotenv"
"github.com/mix-go/xcli/flag"
"github.com/mix-go/xcli/process"
"os"
"os/signal"
"strings"
"syscall"
"time"
)
type APICommand struct{}func (t *APICommand) Main(a) {
if flag.Match("d"."daemon").Bool() {
process.Daemon()
}
logger := di.Logrus()
server := di.Server()
addr := dotenv.Getenv("GIN_ADDR").String(": 8080")
mode := dotenv.Getenv("GIN_MODE").String(gin.ReleaseMode)
// server
gin.SetMode(mode)
router := gin.New()
routes.SetRoutes(router)
server.Addr = flag.Match("a"."addr").String(addr)
server.Handler = router
// signal
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
go func(a) {
<-ch
logger.Info("Server shutdown")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
iferr := server.Shutdown(ctx); err ! =nil {
logger.Errorf("Server shutdown error: %s", err)
}
}()
// logger
ifmode ! = gin.ReleaseMode { handlerFunc := gin.LoggerWithConfig(gin.LoggerConfig{ Formatter:func(params gin.LogFormatterParams) string {
return fmt.Sprintf("%s|%s|%d|%s",
params.Method,
params.Path,
params.StatusCode,
params.ClientIP,
)
},
Output: logger.Out,
})
router.Use(handlerFunc)
}
// run
welcome()
logger.Infof("Server start at %s", server.Addr)
iferr := server.ListenAndServe(); err ! =nil && !strings.Contains(err.Error(), "http: Server closed") {
panic(err)
}
}
Copy the code
Configure routes in the routes/main.go file:
Some common examples are already included. You just need to add routes here to start development
package routes
import (
"github.com/gin-gonic/gin"
"github.com/mix-go/api-skeleton/controllers"
"github.com/mix-go/api-skeleton/middleware"
)
func SetRoutes(router *gin.Engine) {
router.Use(gin.Recovery()) // error handle
router.GET("hello",
middleware.CorsMiddleware(),
func(ctx *gin.Context) {
hello := controllers.HelloController{}
hello.Index(ctx)
},
)
router.POST("users/add",
middleware.AuthMiddleware(),
func(ctx *gin.Context) {
hello := controllers.UserController{}
hello.Add(ctx)
},
)
router.POST("auth".func(ctx *gin.Context) {
auth := controllers.AuthController{}
auth.Index(ctx)
})
}
Copy the code
Next we compile the above program:
- linux & macOS
go build -o bin/go_build_main_go main.go
Copy the code
- win
go build -o bin/go_build_main_go.exe main.go
Copy the code
Start the server
$ bin/go_build_main_go api ___ ______ ___ _ /__ ___ _____ ______ / __ `__ \/ /\ \/ /__ __ `/ __ \ / / / / / / / /\ \/ _ /_/ // /_/ / /_/ /_/ /_/_/ /_/\_\ \__, / \____/ /____/ Server Name: mix-api Listen Addr: :8080 System Name: Darwin Go Version: 1.13.4 Framework Version: Level =info MSG =Server start file=api.go:58Copy the code
Write a Web service
First we create a project skeleton using the mixcli command:
$mixcli new hello Use the arrow keys to navigate: ↓ ↑ → ←? Select Project Type: CLI API ▸ Web (contains the websocket) gRPCCopy the code
Generate skeleton directory structure as follows:
. ├ ─ ─ the README. Md ├ ─ ─ bin ├ ─ ─ commands ├ ─ ─ the conf ├ ─ ─ configor ├ ─ ─ controllers ├ ─ ─ di ├ ─ ─ dotenv ├ ─ ─. Mod ├ ─ ─. Sum ├ ─ ─ └. Go └── ─ Middleware ├─ public └─ Routes ├─ Runtime ├─Copy the code
Mian. Go file:
xcli.AddCommand
Method passed incommands.Commands
Defines all commands
package main
import (
"github.com/mix-go/web-skeleton/commands"
_ "github.com/mix-go/web-skeleton/configor"
_ "github.com/mix-go/web-skeleton/di"
_ "github.com/mix-go/web-skeleton/dotenv"
"github.com/mix-go/dotenv"
"github.com/mix-go/xcli"
)
func main(a) {
xcli.SetName("app").
SetVersion("0.0.0 - alpha").
SetDebug(dotenv.Getenv("APP_DEBUG").Bool(false)) xcli.AddCommand(commands.Commands...) .Run() }Copy the code
Commands/main. Go file:
We can customize commands here to see more
RunI
Specifies the interface for executing the commandRun
Set an anonymous function
package commands
import (
"github.com/mix-go/xcli"
)
var Commands = []*xcli.Command{
{
Name: "web",
Short: "\tStart the web server",
Options: []*xcli.Option{
{
Names: []string{"a"."addr"},
Usage: "\tListen to the specified address",
},
{
Names: []string{"d"."daemon"},
Usage: "\tRun in the background",
},
},
RunI: &WebCommand{},
},
}
Copy the code
Commands/web. Go file:
The business code is written in the main method of the WebCommand structure, and the generated code already contains:
- Listening signal stops service
- Logs are printed based on the mode
- Optional background daemon execution
Basically, it can be used online without modification
package commands
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/mix-go/dotenv"
"github.com/mix-go/web-skeleton/di"
"github.com/mix-go/web-skeleton/routes"
"github.com/mix-go/xcli"
"github.com/mix-go/xcli/flag"
"github.com/mix-go/xcli/process"
"os"
"os/signal"
"strings"
"syscall"
"time"
)
type WebCommand struct{}func (t *WebCommand) Main(a) {
if flag.Match("d"."daemon").Bool() {
process.Daemon()
}
logger := di.Logrus()
server := di.Server()
addr := dotenv.Getenv("GIN_ADDR").String(": 8080")
mode := dotenv.Getenv("GIN_MODE").String(gin.ReleaseMode)
// server
gin.SetMode(mode)
router := gin.New()
routes.SetRoutes(router)
server.Addr = flag.Match("a"."addr").String(addr)
server.Handler = router
// signal
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
go func(a) {
<-ch
logger.Info("Server shutdown")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
iferr := server.Shutdown(ctx); err ! =nil {
logger.Errorf("Server shutdown error: %s", err)
}
}()
// logger
ifmode ! = gin.ReleaseMode { handlerFunc := gin.LoggerWithConfig(gin.LoggerConfig{ Formatter:func(params gin.LogFormatterParams) string {
return fmt.Sprintf("%s|%s|%d|%s",
params.Method,
params.Path,
params.StatusCode,
params.ClientIP,
)
},
Output: logger.Out,
})
router.Use(handlerFunc)
}
// templates
router.LoadHTMLGlob(fmt.Sprintf("%s/.. /templates/*", xcli.App().BasePath))
// static file
router.Static("/static", fmt.Sprintf("%s/.. /public/static", xcli.App().BasePath))
router.StaticFile("/favicon.ico", fmt.Sprintf("%s/.. /public/favicon.ico", xcli.App().BasePath))
// run
welcome()
logger.Infof("Server start at %s", server.Addr)
iferr := server.ListenAndServe(); err ! =nil && !strings.Contains(err.Error(), "http: Server closed") {
panic(err)
}
}
Copy the code
Configure routes in the routes/main.go file:
Some common examples are already included. You just need to add routes here to start development
package routes
import (
"github.com/gin-gonic/gin"
"github.com/mix-go/web-skeleton/controllers"
"github.com/mix-go/web-skeleton/middleware"
)
func SetRoutes(router *gin.Engine) {
router.Use(gin.Recovery()) // error handle
router.GET("hello".func(ctx *gin.Context) {
hello := controllers.HelloController{}
hello.Index(ctx)
},
)
router.Any("users/add",
middleware.SessionMiddleware(),
func(ctx *gin.Context) {
user := controllers.UserController{}
user.Add(ctx)
},
)
router.Any("login".func(ctx *gin.Context) {
login := controllers.LoginController{}
login.Index(ctx)
})
router.GET("websocket".func(ctx *gin.Context) {
ws := controllers.WebSocketController{}
ws.Index(ctx)
},
)
}
Copy the code
Next we compile the above program:
- linux & macOS
go build -o bin/go_build_main_go main.go
Copy the code
- win
go build -o bin/go_build_main_go.exe main.go
Copy the code
Command line to start the Web server:
$ bin/go_build_main_go web ___ ______ ___ _ /__ ___ _____ ______ / __ `__ \/ /\ \/ /__ __ `/ __ \ / / / / / / / /\ \/ _ /_/ // /_/ / /_/ /_/ /_/_/ /_/\_\ \__, / \____/ /____/ Server Name: mix-web Listen Addr: :8080 System Name: Darwin Go Version: 1.13.4 Framework Version: Level =info MSG =Server start file=web.go:58Copy the code
Browser tests:
- The first browser to enter http://127.0.0.1:8080/login for the session
- Submit the form after the jump to http://127.0.0.1:8080/users/add pages
Write a WebSocket service
WebSocket does the handshake over HTTP, so when we write code, it’s pretty much the same as when we write a Web project, except when the request comes in, we need to use a WebSocket upgrader to upgrade the request to a WebSocket connection, Next comes the logical handling of the connection, which is consistent with traditional Socket operations from here on out.
The routes/main.go file defines a WebSocket route:
router.GET("websocket".func(ctx *gin.Context) {
ws := controllers.WebSocketController{}
ws.Index(ctx)
},
)
Copy the code
Controllers/ws. Go file:
- Created a
upgrader
The upgrader will upgrade to a WebSocket connection when the request comes in - Defines a
WebSocketSession
Is responsible for managing the entire life cycle of the connection session.Start()
Two coroutines are started to handle message reading and writing respectively- In the message read coroutine, started
WebSocketHandler
Structure of theIndex
Method to process the message, in the actual project we can use different structure according to different message content to process, to achieve the controller function of Web project
package controllers
import (
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/mix-go/web-skeleton/di"
"github.com/mix-go/xcli"
"net/http"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,}type WebSocketController struct{}func (t *WebSocketController) Index(c *gin.Context) {
logger := di.Logrus()
if xcli.App().Debug {
upgrader.CheckOrigin = func(r *http.Request) bool {
return true
}
}
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
iferr ! =nil {
logger.Error(err)
c.Status(http.StatusInternalServerError)
c.Abort()
return
}
session := WebSocketSession{
Conn: conn,
Header: c.Request.Header,
Send: make(chan []byte.100),
}
session.Start()
server := di.Server()
server.RegisterOnShutdown(func(a) {
session.Stop()
})
logger.Infof("Upgrade: %s", c.Request.UserAgent())
}
type WebSocketSession struct {
Conn *websocket.Conn
Header http.Header
Send chan []byte
}
func (t *WebSocketSession) Start(a) {
go func(a) {
logger := di.Logrus()
for {
msgType, msg, err := t.Conn.ReadMessage()
iferr ! =nil {
if! websocket.IsCloseError(err,1001.1006) {
logger.Error(err)
}
t.Stop()
return
}
ifmsgType ! = websocket.TextMessage {continue
}
handler := WebSocketHandler{
Session: t,
}
handler.Index(msg)
}
}()
go func(a) {
logger := di.Logrus()
for {
msg, ok := <-t.Send
if! ok {return
}
iferr := t.Conn.WriteMessage(websocket.TextMessage, msg); err ! =nil {
logger.Error(err)
t.Stop()
return(1)}}}}func (t *WebSocketSession) Stop(a) {
defer func(a) {
if err := recover(a); err ! =nil {
logger := di.Logrus()
logger.Error(err)
}
}()
close(t.Send)
_ = t.Conn.Close()
}
type WebSocketHandler struct {
Session *WebSocketSession
}
func (t *WebSocketHandler) Index(msg []byte) {
t.Session.Send <- []byte("hello, world!")}Copy the code
Next we compile the above program:
- linux & macOS
go build -o bin/go_build_main_go main.go
Copy the code
- win
go build -o bin/go_build_main_go.exe main.go
Copy the code
Start the Web server from the command line:
$ bin/go_build_main_go web ___ ______ ___ _ /__ ___ _____ ______ / __ `__ \/ /\ \/ /__ __ `/ __ \ / / / / / / / /\ \/ _ /_/ // /_/ / /_/ /_/ /_/_/ /_/\_\ \__, / \____/ /____/ Server Name: mix-web Listen Addr: :8080 System Name: Darwin Go Version: 1.13.4 Framework Version: Level =info MSG =Server start file=web.go:58Copy the code
Browser tests:
- We use off-the-shelf tools test: www.easyswoole.com/wstool.html
Write a gRPC service, client
First we create a project skeleton using the mixcli command:
$mixcli new hello Use the arrow keys to navigate: ↓ ↑ → ←? Select Project Type: CLI API Web (contains the websocket) ▸ gRPCCopy the code
Generate skeleton directory structure as follows:
. ├ ─ ─ the README. Md ├ ─ ─ bin ├ ─ ─ commands ├ ─ ─ the conf ├ ─ ─ configor ├ ─ ─ di ├ ─ ─ dotenv ├ ─ ─. Mod ├ ─ ─. Sum ├ ─ ─ main. Go ├ ─ ─ Protos exercises ── exercisesCopy the code
Mian. Go file:
xcli.AddCommand
Method passed incommands.Commands
Defines all commands
package main
import (
"github.com/mix-go/dotenv"
"github.com/mix-go/grpc-skeleton/commands"
_ "github.com/mix-go/grpc-skeleton/configor"
_ "github.com/mix-go/grpc-skeleton/di"
_ "github.com/mix-go/grpc-skeleton/dotenv"
"github.com/mix-go/xcli"
)
func main(a) {
xcli.SetName("app").
SetVersion("0.0.0 - alpha").
SetDebug(dotenv.Getenv("APP_DEBUG").Bool(false)) xcli.AddCommand(commands.Commands...) .Run() }Copy the code
Commands/main. Go file:
We can customize commands here to see more
- Defines the
grpc:server
,grpc:client
Two subcommands RunI
Specifies the interface for executing the commandRun
Set an anonymous function
package commands
import (
"github.com/mix-go/xcli"
)
var Commands = []*xcli.Command{
{
Name: "grpc:server",
Short: "gRPC server demo",
Options: []*xcli.Option{
{
Names: []string{"d"."daemon"},
Usage: "Run in the background",
},
},
RunI: &GrpcServerCommand{},
},
{
Name: "grpc:client",
Short: "gRPC client demo",
RunI: &GrpcClientCommand{},
},
}
Copy the code
Protos /user.proto data structure file:
The go code generated by.proto needs to be used in both client and server code because both sides need to communicate using this data structure
.proto
是 gRPCCommunication data structure file, usingprotobufagreement
syntax = "proto3"; package go.micro.grpc.user; option go_package = ".; protos"; service User { rpc Add(AddRequest) returns (AddResponse) {} } message AddRequest { string Name = 1; } message AddResponse { int32 error_code = 1; string error_message = 2; int64 user_id = 3; }Copy the code
Then we need to install the grPC-related compiler:
- www.cnblogs.com/oolo/p/1184…
Next we compile the.proto file:
- After a successful compilation, it is generated in the current directory
protos/user.pb.go
file
cd protos
protoc --go_out=plugins=grpc:. user.proto
Copy the code
Commands/server. Go file:
The server code is written in the main method of the GrpcServerCommand structure. The generated code already contains:
- Listening signal stops service
- Optional background daemon execution
pb.RegisterUserServer
By registering a default service, users can simply extend their own service
package commands
import (
"github.com/mix-go/dotenv"
"github.com/mix-go/grpc-skeleton/di"
pb "github.com/mix-go/grpc-skeleton/protos"
"github.com/mix-go/grpc-skeleton/services"
"github.com/mix-go/xcli/flag"
"github.com/mix-go/xcli/process"
"google.golang.org/grpc"
"net"
"os"
"os/signal"
"strings"
"syscall"
)
var listener net.Listener
type GrpcServerCommand struct{}func (t *GrpcServerCommand) Main(a) {
if flag.Match("d"."daemon").Bool() {
process.Daemon()
}
addr := dotenv.Getenv("GIN_ADDR").String(": 8080")
logger := di.Logrus()
// listen
listener, err := net.Listen("tcp", addr)
iferr ! =nil {
panic(err)
}
listener = listener
// signal
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
go func(a) {
<-ch
logger.Info("Server shutdown")
iferr := listener.Close(); err ! =nil {
panic(err)
}
}()
// server
s := grpc.NewServer()
pb.RegisterUserServer(s, &services.UserService{})
// run
welcome()
logger.Infof("Server run %s", addr)
iferr := s.Serve(listener); err ! =nil && !strings.Contains(err.Error(), "use of closed network connection") {
panic(err)
}
}
Copy the code
Services/user. Go file:
The services.UserService{} service code registered in the server code is as follows:
Just populate the business logic
package services
import (
"context"
pb "github.com/mix-go/grpc-skeleton/protos"
)
type UserService struct{}func (t *UserService) Add(ctx context.Context, in *pb.AddRequest) (*pb.AddResponse, error) {
// Perform database operations
// ...
resp := pb.AddResponse{
ErrorCode: 0,
ErrorMessage: "",
UserId: 10001,}return &resp, nil
}
Copy the code
Commands/client. Go file:
The client code is written in the main method of the GrpcClientCommand structure. The generated code already contains:
- Obtain the server connection address from environment configuration
- Set the
5s
The execution timeout of
package commands
import (
"context"
"fmt"
"github.com/mix-go/dotenv"
pb "github.com/mix-go/grpc-skeleton/protos"
"google.golang.org/grpc"
"time"
)
type GrpcClientCommand struct{}func (t *GrpcClientCommand) Main(a) {
addr := dotenv.Getenv("GIN_ADDR").String(": 8080")
ctx, _ := context.WithTimeout(context.Background(), time.Duration(5)*time.Second)
conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure(), grpc.WithBlock())
iferr ! =nil {
panic(err)
}
defer func(a) {
_ = conn.Close()
}()
cli := pb.NewUserClient(conn)
req := pb.AddRequest{
Name: "xiaoliu",
}
resp, err := cli.Add(ctx, &req)
iferr ! =nil {
panic(err)
}
fmt.Println(fmt.Sprintf("Add User: %d", resp.UserId))
}
Copy the code
Next we compile the above program:
- linux & macOS
go build -o bin/go_build_main_go main.go
Copy the code
- win
go build -o bin/go_build_main_go.exe main.go
Copy the code
Start GRPC from the command line :server
$ bin/go_build_main_go grpc:server ___ ______ ___ _ /__ ___ _____ ______ / __ `__ \/ /\ \/ /__ __ `/ __ \ / / / / / / / /\ \/ _ /_/ // /_/ / /_/ /_/ /_/_/ /_/\_\ \__, / \____/ /____/ Server Name: mix-grpc Listen Addr: :8080 System Name: Darwin Go Version: 1.13.4 Framework Version: 1.0.20 time=2020-11-09 15:08:17.544 level=info MSG =Server run :8080 file=server.go:46Copy the code
Then start a new terminal and execute the following client commands to communicate with the server above
$ bin/go_build_main_go grpc:client
Add User: 10001
Copy the code
How to use Logger, Database, Redis and other components in the DI container
The common components to be used in the project are defined in the DI directory. The framework generates some common components by default. Users can also define their own components
- Where can I use it
It can be used anywhere in the code, but in order to use environment variables and custom configurations, we usually use it in Run and RunI defined by the xcli.mand structure.
- Use logs, such as:
logrus
,zap
logger := di.Logrus()
logger.Info("test")
Copy the code
- Use a database, such as:
gorm
,xorm
db := di.Gorm()
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user)
fmt.Println(result)
Copy the code
- Use Redis, for example:
go-redis
rdb := di.GoRedis()
val, err := rdb.Get(context.Background(), "key").Result()
iferr ! =nil {
panic(err)
}
fmt.Println("key", val)
Copy the code
Rely on
The official library
- Github.com/mix-go/mixc…
- github.com/mix-go/xcli
- github.com/mix-go/xdi
- github.com/mix-go/xwp
- github.com/mix-go/xfmt
- Github.com/mix-go/dote…
Third-party libraries
- Github.com/gin-gonic/g…
- gorm.io
- Github.com/go-redis/re…
- Github.com/jinzhu/conf…
- github.com/uber-go/zap
- Github.com/sirupsen/lo…
- Github.com/natefinch/l…
- Github.com/lestrrat-go…
- Github.com/go-session/…
- Github.com/go-session/…
- Github.com/dgrijalva/j…
- Github.com/gorilla/web…
- github.com/golang/grpc
- Github.com/golang/prot…
License
Apache License Version 2.0, www.apache.org/licenses/