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

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

lcy2019-11-12技术文章6076

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发布,如需转载请注明出处。

本文链接:http://www.c3389.com/post/456.html

相关文章

一句话判断数据库是否web和数据库分离

and exists(select * from admin where 1=(SELECT (case when host_name()=@@servername then 1 else 0 end...

asp个人主页安全总结

作者:outstand   来源:邪恶八进制信息安全团队前言:最近在做毕业设计,一同学问我asp做的站应该注意哪些安全问题,我纳闷了好久,我自己都没考虑这个问题的,哎,这同学也太认...

手工日志备份一句话

有时候工具不能成功 需要手工代码如下:1.InjectionURL';alter database sq_huaweitoys set RECOVERY FULL-- (把sql设置成日志完全恢复模式...

Windows XP不能打开chm文件解决方法汇总

如果Windows XP不能打开chm格式的文件,1。如果提示是:如果提示是Internet Explorer 不能链接到您请求的网页或者打开后“页面无法显示”。请下载jjhd...

提权旧曲新歌

 说起WebShell提权的方法网上文章是多得所处可见!那么在这里我写点自己提权时所用到几个小方法!也是自己实战得来的经验!方法如有不对的请大家多多指点!提权时要到的工具如下:cmd.exe...

读取注册表RUN项里面有几个键值的方法

一、批处理:(保存为.bat或.cmd文件)  1、利用reg query:  @echo off  set r=\Software\Microsoft\Windows\...

发表评论

访客

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