cy's Blog

好好学习 天天上网

Golang:在一个端口上运行多种服务

Ever faced the problem of having multiple ports in an application, one for each service? In this post, I’m going to brief about how to run multiple services via the same listener port.

At Dgraph , we used to have one port to serve HTTP requests, one for gRPC and one more for internal communication among the servers. But now we just use one port for all the outside facing services and one for internal server communications.

Cmux is a connection multiplexing library for Go. It allows you to differentiate services based on the payload. Hence, you can serve HTTP, HTTPS, gRPC, etc on the same port. For complete information on the protocols supported, refer to their godoc.

Let us jump into the three simple steps with some code sample and get this working in a jiffy.

First setup the different services as you would usually do. In our case we setup a gRPC service and an HTTP handler function.

// Setup gRPC server.type grpcServer struct{}

func (s *grpcServer) Query(ctx context.Context,
  req *graph.Request) (*graph.Response, error) {
  .
  .
  .
}// Handler function for http/https queries.func queryHandler(w http.ResponseWriter, r *http.Request) {
  addCorsHeaders(w)
  .
  .
  .

}

Second, write separate functions to start each service using a net.Listener object as if it is the only service using that listener. Later we’ll multiplex a single TCP listener into multiple listeners.

// Wrapper functions to start serving different services.func serveGRPC(l net.Listener) {
  s := grpc.NewServer(grpc.CustomCodec(&query.Codec{}))
  graph.RegisterDgraphServer(s, &grpcServer{})  if err := s.Serve(l); err != nil {    log.Fatalf("While serving gRpc request: %v", err)
  }
}

func serveHTTP(l net.Listener) {  if err := http.Serve(l, nil); err != nil {    log.Fatalf("While serving http request: %v", err)
  }
}

Third, create a listener object and multiplex it using a cmux matcher. It’ll read the header bytes of exchanges and figure out which service to trigger by giving us a new sub-listener (We just call it that, though it’s actually just net.Listener) for every match. We then call the services that we wrote earlier with these corresponding sub-listeners. Look at the following code sample to get a better hang of the above-mentioned steps.

func setupServer() {
  go worker.RunServer(*workerPort) // For internal communication.

  // Create a listener at the desired port.
  l, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))  if err != nil {    log.Fatal(err)
  }  // Create a cmux object.
  tcpm := cmux.New(l)  // Declare the match for different services required.
  httpl := tcpm.Match(cmux.HTTP1Fast())
  grpcl := tcpm.MatchWithWriters(
    cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc"))
  http2 := tcpm.Match(cmux.HTTP2())  // Link the endpoint to the handler function.
  http.HandleFunc("/query", queryHandler)  // Initialize the servers by passing in the custom listeners (sub-listeners).
  go serveGRPC(grpcl)
  go serveHTTP(httpl)
  go serveHTTP(http2)  // Close the listener when done.
  go func() {
    <-closeCh    // Stops listening further but already accepted connections are not closed.
    l.Close()
  }()  log.Println("grpc server started.")  log.Println("http server started.")  log.Println("Server listening on port", *port)  // Start cmux serving.
  if err := tcpm.Serve(); !strings.Contains(err.Error(),    "use of closed network connection") {    log.Fatal(err)
  }
}

So, there we have it. A single port to cater to many services that you might be using.

Hope you had fun with this post and learnt something new. Thanks for reading and do let us know your thoughts and how it works out for you.

来源:https://blog.dgraph.io/post/cmux/

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

«   2020年5月   »
123
45678910
11121314151617
18192021222324
25262728293031
搜索
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
标签列表
    作者列表
    站点信息
    • 文章总数:463
    • 页面总数:2
    • 分类总数:10
    • 标签总数:0
    • 评论总数:2015
    • 浏览总数:665607

    Powered By Z-BlogPHP 1.6.0 Valyria

    Copyright 2008-2017 lcy. Some Rights Reserved.