If you ever have the need to combine a plain HTTP webserver with a gRPC server on the same port, then you can use cmux to do so.
You can do something like:
1package main
2
3import (
4 "log"
5 "net"
6 "net/http"
7
8 "github.com/soheilhy/cmux"
9 "golang.org/x/sync/errgroup"
10 "google.golang.org/grpc"
11)
12
13func main() {
14
15 // Create the listener
16 l, err := net.Listen("tcp", ":8080")
17 if err != nil {
18 log.Fatal(err)
19 }
20
21 // Create a new cmux instance
22 m := cmux.New(l)
23
24 // Create a grpc listener first
25 grpcListener := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
26
27 // All the rest is assumed to be HTTP
28 httpListener := m.Match(cmux.Any())
29
30 // Create the servers
31 grpcServer := grpc.NewServer()
32 httpServer := &http.Server{}
33
34 // Use an error group to start all of them
35 g := errgroup.Group{}
36 g.Go(func() error {
37 return grpcServer.Serve(grpcListener)
38 })
39 g.Go(func() error {
40 return httpServer.Serve(httpListener)
41 })
42 g.Go(func() error {
43 return m.Serve()
44 })
45
46 // Wait for them and check for errors
47 err = g.Wait()
48 if err != nil {
49 log.Fatal(err)
50 }
51
52}
This also demonstrates how the errgroup module can be used. The Wait
method blocks until all function calls from the Go
method have returned, then returns the first non-nil error (if any) from them.
PS: the example itself doesn't actually do something useful as we didn't register any gRPC functions neither did I define HTTP handlers.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.