Coder Social home page Coder Social logo

WebSocket吞吐量 about nbio HOT 26 CLOSED

lesismal avatar lesismal commented on May 17, 2024
WebSocket吞吐量

from nbio.

Comments (26)

lesismal avatar lesismal commented on May 17, 2024

因为仓库自带benchmark数据跟其他人的软硬件环境有差别,所以仓库自带的benchmark数据对于其他人而言未必准确,我的另一个项目arpc就自己环境下跑的数据跟其他benchmark库的结论不太一样,所以我的仓库只提供代码不展示性能数据。

这里有websocket百万连接数的简单测试例子,楼主可以根据你们的配置和需要改改跑一下测试看看效果:
https://github.com/lesismal/nbio-examples/tree/master/websocket_1m
https://github.com/lesismal/nbio-examples/tree/master/websocket_1m_tls

另外,基于标准库的框架每个连接至少一个协程(websocket可能需要两个协程来避免广播场景时单个连接网络不佳导致线头阻塞)在海量并发场景下的协程数量、内存、GC、STW、调度等成本太高,硬件成本、服务稳定性差。nbio主要用于优化高并发场景来达到低消耗和稳定性,在线数越大越可以节省大量硬件,一般数量的在线数未必比基于标准库的方案强,请以自家业务需要实测为准。

from nbio.

egmkang avatar egmkang commented on May 17, 2024

其实我比较关心pps, 比如普通的千兆网, 能不能跑到七八十万pps

from nbio.

egmkang avatar egmkang commented on May 17, 2024
package main

import (
	"context"
	"fmt"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"net/http"
	"sync/atomic"
	"time"
)

var count int64 = 0

func qps() {
	var oldCount = count
	for {
		time.Sleep(time.Second)
		var newCount = atomic.LoadInt64(&count)
		if newCount != oldCount {
			println(fmt.Sprintf("%d/qps", newCount-oldCount))
		}
		oldCount = newCount
	}
}

func wsClientLoop() {
	time.Sleep(time.Second * 8)
	conn, _, _, err := ws.Dial(context.Background(), "ws://127.0.0.1:8888/ws")
	if err != nil {
		return
	}
	defer conn.Close()
	wsutil.WriteClientMessage(conn, ws.OpBinary, []byte("121212"))
	for {
		msg, op, e := wsutil.ReadServerData(conn)
		if e != nil {
			break
		}
		e = wsutil.WriteClientMessage(conn, op, msg)
		if e != nil {
			break
		}
	}
}

func wsLoop(w http.ResponseWriter, r *http.Request) {
	conn, _, _, err := ws.UpgradeHTTP(r, w)
	if err != nil {
		return
	}
	go func() {
		defer conn.Close()
		for {
			msg, op, e := wsutil.ReadClientData(conn)
			if e != nil {
				break
			}
			atomic.AddInt64(&count, 1)
			e = wsutil.WriteServerMessage(conn, op, msg)
			if e != nil {
				break
			}
		}
	}()
}

func main() {
	go qps()
	for i := 0; i < 32; i += 1 {
		go wsClientLoop()
	}
	http.HandleFunc("/ws", wsLoop)
	http.ListenAndServe("0.0.0.0:8888", nil)
}

gobwas只能支持大概30w个来回

package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"net/url"
	"sync/atomic"
	"time"
)

var upgrader = websocket.Upgrader{}

var count int64 = 0

func qps() {
	var oldCount = count
	for {
		time.Sleep(time.Second)
		var newCount = atomic.LoadInt64(&count)
		if newCount != oldCount {
			println(fmt.Sprintf("%d/qps", newCount-oldCount))
		}
		oldCount = newCount
	}
}

func wsClientLoop() {
	time.Sleep(time.Second * 8)
	u := url.URL{Scheme: "ws", Host: "127.0.0.1:8888", Path: "/ws"}
	conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		return
	}
	defer conn.Close()
	conn.WriteMessage(websocket.BinaryMessage, []byte("121212"))
	for {
		mt, message, err := conn.ReadMessage()
		if err != nil {
			break
		}
		err = conn.WriteMessage(mt, message)
		if err != nil {
			break
		}
	}
}

func wsLoop(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	defer conn.Close()
	for {
		mt, message, err := conn.ReadMessage()
		if err != nil {
			break
		}
		atomic.AddInt64(&count, 1)
		err = conn.WriteMessage(mt, message)
		if err != nil {
			break
		}
	}
}

func main() {
	go qps()
	for i := 0; i < 32; i += 1 {
		go wsClientLoop()
	}
	http.HandleFunc("/ws", wsLoop)
	http.ListenAndServe("0.0.0.0:8888", nil)
}

gorilla大概50Wqps个来回

from nbio.

lesismal avatar lesismal commented on May 17, 2024

nbio主要针对海量并发场景的资源消耗优化,海量并发场景下节省更多硬件并且服务更加稳定,如果只是普通连接数,比如一两万个连接数,基于标准库的方案内存成本也不高,建议用标准库方案。

如果连接数很大比如50k 100k 1000k连接数,nbio才会有优势并且优势可能比较明显。

实际的性能数据,要以你们自己的业务为准,不同的业务类型对各项指标的需求差异还是比较大的,比如你单节点就10k在线,标准库方案10k/20k个协程内存、GC、调度成本也不会爆,就用标准库好了,gorilla足够了,不要用melody,melody有个并发bug小概率会panic,我提交了pr但是作者好像已经很久没维护了

另外,不要用gobwas,楼主有兴趣可以去gobwas/ws和那个gobwas/ws-examples里搜下与我有关的issue,gobwas/ws+netpoll是存在明显缺陷的。

至于性能数据,ubuntu 18.04 虚拟机 4c8t i78700 8G localhost "hello world"简单payload 对比:

连接数 gorilla nbio
10k 26-28w qps 16-18w qps
20k 22-24w qps 16-18w qps
100k STW明显,平均2-5w qps 但几千到几万跳动,内存1.6G 14-16w qps,运行稳定,内存占用310M

要根据实际业务的在线数、项目软硬件成本考量等综合因素来考虑框架选型。

from nbio.

lesismal avatar lesismal commented on May 17, 2024

千兆/8理论上125M吞吐量,小包加上各个协议栈header size应该百字节以内,理论上应该是可以跑到百万,还得看实际网卡型号和工作表现。
所以要综合看你测试时的各项软硬件指标,如果网卡还没跑满、而是cpu满了那换框架还可能提升(比如连接数很大,可以改用nbio优化),但是看你例子好像只有32个链接,那nbio应该不会提升,或者如果是网卡已经跑满了那nbio也难,还是以你自己的环境实测为准的好。

from nbio.

lesismal avatar lesismal commented on May 17, 2024

另外,相比gorilla,nbio的websocket.Conn是非阻塞的,所以即使广播场景,也不需要担心gorilla的Conn直接写可能线头阻塞的问题,也不需要额外封装读和写,只要注册处理数据的函数、写的时候直接写就行了

from nbio.

egmkang avatar egmkang commented on May 17, 2024

OK

from nbio.

lesismal avatar lesismal commented on May 17, 2024

fasthttp 的 websocket 试过没?

from nbio.

egmkang avatar egmkang commented on May 17, 2024

没试过, 周末看看有没有空试一下

from nbio.

egmkang avatar egmkang commented on May 17, 2024

https://github.com/fasthttp/fastws

这最下面有测试

from nbio.

egmkang avatar egmkang commented on May 17, 2024

https://github.com/fasthttp/websocket
试了这个, 不是很赞
我笔记本上gorilla 14W, 这个13W的样子

from nbio.

lesismal avatar lesismal commented on May 17, 2024

我在考虑用nbhttp现有的http parser套装再支持下直接使用标准库的net.Conn,如果连接数量不多的场景就也可以用标准库net.Conn的阻塞读写借口方案了,刚才简单对比了下http的,标准库16-17w,nbhttp 14-15w,nbhttp没优化+net.Conn 20-21w,fasthttp 25-26w,websocket的还没试、需要改更多

from nbio.

egmkang avatar egmkang commented on May 17, 2024

golang里面做网络库不是很好弄, 语言给的工具太少了

from nbio.

lesismal avatar lesismal commented on May 17, 2024

我在考虑用nbhttp现有的http parser套装再支持下直接使用标准库的net.Conn,如果连接数量不多的场景就也可以用标准库net.Conn的阻塞读写借口方案了,刚才简单对比了下http的,标准库16-17w,nbhttp 14-15w,nbhttp没优化+net.Conn 20-21w,fasthttp 25-26w,websocket的还没试、需要改更多

试过了,直接用现在的nbhttp的非阻塞解析应该是不行,nbhttp直接用net.Conn,http略好于标准库,websocket略差于标准库,因为都是按照非阻塞流解析、支持粘包处理之类的所以性能并不比标准库强。
如果想直接net.Conn超过标准库,我得从头写一套不需要考虑非阻塞的、不需要半包cache之类的逻辑,读写buffer、pool之类的也都可以做更细的优化,重写的话相比于gorilla还有提升空间,但也是很耗体力,而且没法跟nbio现有的兼容,我不打算搞了

from nbio.

lesismal avatar lesismal commented on May 17, 2024

golang里面做网络库不是很好弄, 语言给的工具太少了

你们做什么类型的业务?应该是可以做针对性优化的

from nbio.

egmkang avatar egmkang commented on May 17, 2024

游戏服务器

from nbio.

lesismal avatar lesismal commented on May 17, 2024

游戏服务器

游戏服务器需要高qps吞吐的场景大概几个点:

  1. 单服务节点在线量非常大
  2. MMORPG/MOBA/FPS 等游戏类型消息频率很高
  3. bigworld或者类似bigworld使用统一的 gate/proxy ,gate/proxy 转发量比较大

你们属于哪种?多数问题可以增加节点解决,少量的得优化地图视区、游戏内单位过滤、服务端渲染广播频率之类的发送策略

假设单节点1w连接,50w出包频率,人均50个包,相当于20ms一帧,通常应该足够了

如果是用统一的 gate/proxy 导致转发量很大,但游戏本身不是MMORPG,可以考虑把 gate/proxy 这层去掉

from nbio.

lesismal avatar lesismal commented on May 17, 2024

如果游戏客户端不是web/h5,不用ws直接tcp性能能提升一波,web/h5的也做不了太高性能、大型游戏所以应该也不至于需要这种单节点近百万qps的吧 😂

from nbio.

lesismal avatar lesismal commented on May 17, 2024

这还有个基于标准库的:
github.com/guonaihong/tinyws

但是跑了下,比gorilla也高不了太多

from nbio.

egmkang avatar egmkang commented on May 17, 2024

能把千兆网pps跑满自然是最好的选择, 这样才能给逻辑层预留更多的时间片

from nbio.

lesismal avatar lesismal commented on May 17, 2024

能把千兆网pps跑满自然是最好的选择, 这样才能给逻辑层预留更多的时间片

得看下你们测试时 cpu 负载,如果 cpu 没跑满,同时看下用的网卡型号、驱动、设置,不同型号的网卡未必都能跑得满,硬件也需要调优,可以对比下c/cpp的框架的吞吐量,看看差多少提升空间。

另外一些,比如单纯cpu消耗的类型可以绑定核心,标准库方案一个连接一个协程数量多做不到了,nbio可以设置LockThread, 扩展下也可以调用绑定cpu,然后定制Executor不走协程池handler避免协程、线程的切换,但我没试过实际效果咋样,因为非阻塞流解析本身就比同步解析要复杂些,并且这种得是cpu消耗类型的业务,否则不走协程池,单个handler慢就相当于线头阻塞了,只是网关、代理类的服务适用,handler内有io等慢消耗的不适合。

from nbio.

egmkang avatar egmkang commented on May 17, 2024

其他语言里面, 一般两三根线程就能把千兆网用户态所能支撑的pps跑满, golang这种一个socket两三个协程才能工作的模型, 好浪费CPU

from nbio.

lesismal avatar lesismal commented on May 17, 2024

nbio不是一个连接对应一个或者两三个协程的,是类似c/cpp epoll,io协程池和逻辑协程池,协程数量可控

如果你们的实际场景是网卡跑满,应该是内网服务之间的吧?这种连接数少,可以试试每个连接一个读协程+runtime.LockThread甚至再syscall绑定下cpu,写可以不单独协程

否则默认标准库的方式,调度切换、亲和性差,当然也包括解析器本身的性能(http parser我之前简单对比好像go的比c编译器开O3性能差5-10倍)

虽然说是游戏服务,但是游戏服务的架构方式、实际实现的服务类型也差别很大,归根到底还是cpu/mem/net各种基础设施角度的分析,因为也没给出实际业务逻辑的细节,所以我也没法猜测更多的优化方案,有细节的话还可以琢磨琢磨针对性优化,未必不能优化达到需要

from nbio.

liangmanlin avatar liangmanlin commented on May 17, 2024

目前正想fork一份代码,去改造成游戏服务器的网关部分,有一点点东西想探讨一下

原来我是用标准库的方案做了一个,测试2k并发收发密集数据包,基本没什么大问题

我就想知道,改用nbio,会提升多少,单纯的tcp收发包,不需要进行http解析。

from nbio.

lesismal avatar lesismal commented on May 17, 2024

目前正想fork一份代码,去改造成游戏服务器的网关部分,有一点点东西想探讨一下

原来我是用标准库的方案做了一个,测试2k并发收发密集数据包,基本没什么大问题

我就想知道,改用nbio,会提升多少,单纯的tcp收发包,不需要进行http解析。

这里有个吞吐量测试的仓库可以对比下:
lesismal/go-net-benchmark#1

实际性能可以自己跑下,以你们的软硬件环境为准。

一般2-10k甚至100k这种连接数都不算大——不算大是指标准库方案协程数量、调度、内存、gc成本或压力都在可承受范围内、一般不会达到临界点,连接数很大时标准库方案协程数量太大打破成本平衡、nbio方案优势会明显,连接数小未必比标准库有优势甚至是劣势。当然也看业务类型的特点,实测为准。

from nbio.

liangmanlin avatar liangmanlin commented on May 17, 2024

目前正想fork一份代码,去改造成游戏服务器的网关部分,有一点点东西想探讨一下
原来我是用标准库的方案做了一个,测试2k并发收发密集数据包,基本没什么大问题
我就想知道,改用nbio,会提升多少,单纯的tcp收发包,不需要进行http解析。

这里有个吞吐量测试的仓库可以对比下: lesismal/go-net-benchmark#1

实际性能可以自己跑下,以你们的软硬件环境为准。

一般2-10k甚至100k这种连接数都不算大——不算大是指标准库方案协程数量、调度、内存、gc成本或压力都在可承受范围内、一般不会达到临界点,连接数很大时标准库方案协程数量太大打破成本平衡、nbio方案优势会明显,连接数小未必比标准库有优势甚至是劣势。当然也看业务类型的特点,实测为准。

好的,感谢

from nbio.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.