golang: 技巧---同一个端口监听 http & grpc & websocket 三种不同协议

2019-10-17 12:31:31 +08:00
 guonaihong

用 3 个端口也可以实现类似效果,此篇献给追求完美的你。。。

http & websocket

websocket 用的 http 协议握手,可以通过不同路由区分出 http 还是 websocket。

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"io"
)

var upgrader = websocket.Upgrader{}

func main() {

	r := gin.Default()

	// websocket echo
	r.Any("/websocket", func(c *gin.Context) {
		r := c.Request
		w := c.Writer
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			fmt.Printf("err = %s\n", err)
			return
		}

		defer func() {
			// 发送 websocket 结束包
			conn.WriteMessage(websocket.CloseMessage,
				websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
			// 真正关闭 conn
			conn.Close()
		}()
		// 读取一个包
		mt, d, err := conn.ReadMessage()
		if err != nil {
			fmt.Printf("read fail = %v\n", err)
			return
		}

		fmt.Printf("data:%s\n", d)
		// 写入一个包
		err = conn.WriteMessage(mt, d)
		if err != nil {
			fmt.Printf("write fail = %v\n", err)
			return
		}
	})

	// http echo
	r.GET("/http", func(c *gin.Context) {
		io.Copy(c.Writer, c.Request.Body)
	})

	r.Run()
}

http & grpc

// TODO 晚上

8531 次点击
所在节点    程序员
32 条回复
scukmh
2019-10-17 12:45:07 +08:00
emmmm , 这不是在 Go 高级编程里面有说嘛?
sunny352787
2019-10-17 12:47:32 +08:00
我?我还以为什么黑科技呢...你这数据基本操作吧?
xmge
2019-10-17 12:48:37 +08:00
额。。。。。。这个不本来就是这样吗
guonaihong
2019-10-17 12:52:21 +08:00
@scukmh 有 http & grpc 的?
guonaihong
2019-10-17 12:53:09 +08:00
@sunny352787 http & grpc 是你要的黑科技。
guonaihong
2019-10-17 12:53:45 +08:00
@xmge 还没写完。。。
reus
2019-10-17 13:27:58 +08:00
https://godoc.org/net/http#Hijacker

有啥黑科技的,hijack 之后就是个 net.Conn 了,干什么都随你了。
guonaihong
2019-10-17 13:29:03 +08:00
@reus hijacker 不适用 http2。
reus
2019-10-17 13:40:30 +08:00
@guonaihong 实现一个 net.Listener,Accept 返回 hijack 的 net.Conn,然后将这个 listener 传给 http.Server.Serve 承载 grpc。
dongxiaozhuo
2019-10-17 13:48:17 +08:00
一直不太理解,为什么会有把 gRPC 和 HTTP 刚才一个端口下的诉求…
scukmh
2019-10-17 13:50:07 +08:00
```go
func main() {
...

http.ListenAndServeTLS(port, "server.crt", "server.key",
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.ProtoMajor != 2 {
mux.ServeHTTP(w, r)
return
}
if strings.Contains(
r.Header.Get("Content-Type"), "application/grpc",
) {
grpcServer.ServeHTTP(w, r) // gRPC Server
return
}

mux.ServeHTTP(w, r)
return
}),
)
}
```
想看看您能搞出什么花来
guonaihong
2019-10-17 13:50:30 +08:00
@scukmh @sunny352787 @xmge @reus 好了。
scukmh
2019-10-17 13:50:52 +08:00
v2ex 连 markdown 都不支持了嘛?
guonaihong
2019-10-17 13:52:38 +08:00
@scukmh 谢了,我试下。
reus
2019-10-17 13:52:49 +08:00
@dongxiaozhuo 减少一个配置也是好的
scukmh
2019-10-17 13:58:13 +08:00
dongxiaozhuo
2019-10-17 14:17:34 +08:00
@reus 端口数量并不是一个稀缺资源,增加一个配置并不会大量增加程序的复杂性。但是如果 gRPC 和 HTTP 本身在程序有不同的定位,仅仅是为了节省一个端口 /配置项,将两个不应该融合的东西融合到一起,看起来会得不偿失;但是如果 gRPC 和 HTTP 在程序中的定位是一样的,为什么不直接使用 gRPC 和 HTTP 中一个,而要将两个融合到一起?

如果可以提供一个具体的落地场景讨论,那再好不过了。
qwerthhusn
2019-10-17 14:38:23 +08:00
是三种不同协议,但是 grpc 和 ws 都是先建立在 http 上的
Suvigotimor
2019-10-17 15:26:38 +08:00
巧了,我们还真有 grpc 和 http 在同一端口上做区分的需求.....RUA
sunny352787
2019-10-17 15:46:32 +08:00
@guonaihong 这是 golang 的技巧?这是 cmux 的使用...我以为你好歹会补一个 cmux 的实现原理

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/610233

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX