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 晚上

8195 次点击
所在节点    程序员
32 条回复
suriv520
2019-10-17 15:54:12 +08:00
谢谢楼主分享。
个人专的领域不同,楼上同学可以切磋实现细节探讨更多可能,干嘛冷嘲热讽。
guonaihong
2019-10-17 21:00:03 +08:00
@sunny352787 以后再分享吧,最近打算玩下 rust,挺花时间的。
sunny352787
2019-10-17 21:17:37 +08:00
@suriv520 只是看标题党难受而已,我以为会描述一下原理或者最起码自己手撸代码让大家看看是怎么做的,这直接调用 cmux 库,那你直接说发现一个库可以怎么怎么样不就行了?
guonaihong
2019-10-17 21:34:25 +08:00
@sunny352787 我想分享的是在工作中可以使用的套路。而不是玩具代码做法。如果一个方式自己都半生不熟。误导别人就不好了。
sunny352787
2019-10-17 22:17:54 +08:00
@guonaihong 分享没有任何问题,但标题这样写容易让人误会这几个东西之间的关系,毕竟小白还是挺多的,咱们写的多了自然知道 websocket 和 http 是什么,底层都是 tcp,但基础不扎实的就容易迷糊了。

而且这边简单点讲一下通过 tcp 路由的方式区分普通 tcp 流量和 http 流量就好了,上来先写了个 http 路由区分普通 http 流量和 websocket 这肯定被人喷啊,你看前几条回复的不都是觉得这分享的内容不靠谱?中间你还加了个和本文没啥关系的 GitHub 库难免让人觉得你是为了推广加星才来这么个标题党忽悠小白。这可不是正常技术分享应该的做的,反倒是那帮自媒体推广的套路。
guonaihong
2019-10-17 22:56:02 +08:00
@sunny352787 哈哈。。sunny 兄让我不知道说什么好。难道一篇让人开箱即用的技巧。非要扯得高深点才好。非要告诉别人 http 除了是基于 tcp 的。http2 加入 tcp 多路复用,优化 http1.1 pipeline 的问题。http3 将要 使用 udp,解决 tcp 协议栈在内核开销大的问题。这种一堆细节,除了抬高自己,对读者没有任何好处。我喜欢站在大众读者角度,讲些开箱即用的东东。。。尽理追求复杂的事情说简单,简单的东西直接使用。。。 如果你觉得不舒服,我下一篇尽量用更平谈的标题。我无意在这种小事上继续讨论,这种非技术的讨论实在没意思。你下个回答,我也不回答了(特此说明)。
sunny352787
2019-10-17 23:31:00 +08:00
@guonaihong 技术分享也是讲方式方法的,也很期待大家能把自己的东西给大家讲明白。没想扯得多高深,但故弄玄虚就没劲了。不回复无所谓啊,也很期待你接下来的分享。

忙完这段我也发点东西出来,欢迎指正。
sip2u
2019-10-18 10:32:46 +08:00
感谢 lz 分享
Daath
2019-10-18 17:35:22 +08:00
感谢分享,之前有类似的需求,只不过我直接用 nginx 来做这一层转发,
guonaihong
2019-10-19 19:04:22 +08:00
@Daath 厉害厉害,可否分享下 nginx 的做法。。。
Daath
2019-10-20 13:28:15 +08:00
* 思路是差不多的,都是基于 url 的 path 来重定向上游服务
* 不过好像 nginx 还没支持在 http1.x 上识别 http2.0 的样子。我们就还是分了两个端口。这里跟你像把所有协议都 all in 想法不太一样。
* 大概这么配置的

```
ssl_certificate /opt/ssl/nginx-selfsigned.crt;
ssl_certificate_key /opt/ssl/nginx-selfsigned.key;

server {
listen 80 ssl;

location /http/ {
proxy_pass http://upstream-address:8001/;
...

}

location /websocket/ {
proxy_pass http://upstream-address:8002/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
...

}
}

server {
listen 81 ssl http2;

location /testd.HelloWorldSrv {
grpc_pass grpc://upstream-address:8003;
....
}

location /otherd.HelloWorldSrv {
grpc_pass grpc://upstream-address:8004;
....
}
}


```
ikaros
362 天前
@dongxiaozhuo cloudflare 要求代理的 grpc 必须走 443 端口,我的服务同时 host 了 https 服务, 现在就有需求了

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

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

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

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

© 2021 V2EX