当前位置:首页 > 技术文章 > 正文内容

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

lcy1年前 (2019-11-12)技术文章1582

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/

 

版权声明:本文由cy's Blog发布,如需转载请注明出处。

转载请注明出处:https://www.c3389.com/post/456.html

相关文章

hyper-V MAC分配的说明(转)

Hyper-V上运行的虚拟机,我们一般都会去给它绑定网络,分配网卡。默认情况下,当你安装完成Hyper-V角色以后,Hyper-V就准备好了256个MAC地址以供虚拟机使用,这256个MAC地址,也就...

做自己专用cmd

首先自建一个记事本内容如下(可自行修改):@echo off  @echo --------------------------------------------------@echo 今...

解决Ubuntu 18.04使用ROOT账户登录图形界面”认证失败”

解决Ubuntu 18.04使用ROOT账户登录图形界面”认证失败”

前    言在虚拟机中重装了Ubuntu系统,安装VMware Tools时图个方便用图形界面安装的,发现登录root账户时总提示”认证失败”解决方案也比较简单,但是有的配置需要R...

mysql开启外链语句

phpmyadmin  执行 GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTI...

入侵的十个小技巧

  例如我们的目标网址为“http://www.xxxx.cn/forum/index.php”那么我们把地址改为http://www.xxxx.cn/fo...

错误解决办法 ">星外虚拟主机出现错误解决办法

星外虚拟主机出现<authentication mode="Windows"/>错误解决办法authentication 元素(ASP.NET 设置架构) 属性 说明...

发表评论

访客

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