
preface
in the go-zero community, students often ask, is it okay to put and in the same process? how to do it? sometimes, there are also students who put external services and consumption queues in a process. let’s not say whether this usage is reasonable or not, because of the differences in business scenarios and development models of various companies, we will only look at how to solve such problems more elegantly.API gateway
RPC service
example of the problem
let’s use the example of two services, we have two services that need to be started on two different ports within a process. the code is as follows:HTTP
package main
import (
"fmt"
"net/http"
)
func morning(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "morning!")
}
func evening(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "evening!")
}
type Morning struct{}
func (m Morning) Start() {
http.HandleFunc("/morning", morning)
http.ListenAndServe("localhost:8080", nil)
}
func (m Morning) Stop() {
fmt.Println("Stop morning service...")
}
type Evening struct{}
func (e Evening) Start() {
http.HandleFunc("/evening", evening)
http.ListenAndServe("localhost:8081", nil)
}
func (e Evening) Stop() {
fmt.Println("Stop evening service...")
}
func main() {
// todo: start both services here
}
copy the code
the code is simple enough, that is, there is a request interface, a service return, a request interface, and a service return. let’s try to implement itmorning
morning!
evening
evening
first try
to start two services is not to start both services in it? let’s try itmain
func main() {
var morning Morning
morning.Start()
defer morning.Stop()
var evening Evening
evening.Start()
defer evening.Stop()
}
copy the code
after starting, we will use it to verify itcurl
$ curl -i http://localhost:8080/morning
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:10:34 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8
morning!
$ curl -i http://localhost:8081/evening
curl: (7) Failed to connect to localhost port 8081 after 4 ms: Connection refused
copy the code
why is it only successful and not requestable?morning
evening
let’s try adding a print statement insidemain
func main() {
fmt.Println("Start morning service...")
var morning Morning
morning.Start()
defer morning.Stop()
fmt.Println("Start evening service...")
var evening Evening
evening.Start()
defer evening.Stop()
}
copy the code
reboot
$ go run main.go
Start morning service...
copy the code
found that only printed, the original service did not start at all. the reason for this is that because the current is blocked, subsequent code cannot be executed.Start morning service…
evening
morning.Start()
goroutine
second try
that’s when it comes in handy. as the name suggests, it is used to set a set of actions and wait for them to be notified to continue. let’s try it.WaitGroup
WaitGroup
wait
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println("Start morning service...")
var morning Morning
defer morning.Stop()
morning.Start()
}()
go func() {
defer wg.Done()
fmt.Println("Start evening service...")
var evening Evening
defer evening.Stop()
evening.Start()
}()
wg.Wait()
}
copy the code
start it and try it
$ go run main.go
Start evening service...
Start morning service...
copy the code
well, both services are up, let’s verify itcurl
$ curl -i http://localhost:8080/morning
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:28:33 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8
morning!
$ curl -i http://localhost:8081/evening
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:28:36 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8
evening!
copy the code
it’s really all right, and we see that the process we use isWaitGroup
- remember we had several services that
needed to wait
- add services one by one
- wait for all services to end
let’s see how it’s done~go-zero
third attempt
we provide a tool to easily manage the start and stop of multiple services. let’s see how the scene brought into us is done.go-zero
ServiceGroup
import "github.com/zeromicro/go-zero/core/service"
// more code
func main() {
group := service.NewServiceGroup()
defer group.Stop()
group.Add(Morning{})
group.Add(Evening{})
group.Start()
}
copy the code
as you can see, the code is much better readable, and we won’t accidentally miscalculate the number of additions. and it also ensures that the service started first, consistent with the effect, such behavior is convenient for resource cleanup.WaitGroup
ServiceGroup
Stop
defer
ServiceGroup
not only does it manage each service, but it also provides that when a signal is received, it will actively call the methods of each service, for the service, you can exit gracefully, and for the service, you can exit gracefully.Start/Stop
graceful shutdown
SIGTERM
Stop
HTTP
server.Shutdown
gRPC
server.GracefulStop()
summary
ServiceGroup
the implementation of the code is actually relatively simple, with a total of 82 lines of code.
$ cloc core/service/servicegroup.go
------------------------------------------------------------------
Language files blank comment code
------------------------------------------------------------------
Go 1 22 14 82
------------------------------------------------------------------
copy the code
Although the code is short and concise, but every service (Restful, RPC, MQ) is basically managed through it, it can be said that it is very convenient, and the code is worth reading.go-zero
ServiceGroup
project address
welcome to use and star support us!go-zero