Giter VIP home page Giter VIP logo

allenxuxu / gev Goto Github PK

View Code? Open in Web Editor NEW
1.7K 41.0 193.0 585 KB

🚀Gev is a lightweight, fast non-blocking TCP network library / websocket server based on Reactor mode. Support custom protocols to quickly and easily build high-performance servers.

License: MIT License

Go 98.72% Shell 1.19% Makefile 0.09%
epoll-tcp-server kqueue golang reactor nonblocking epoll network-programming event-driven go gev

gev's Introduction

gev

Github Actions Go Report Card Codacy Badge GoDoc LICENSE Code Size Sourcegraph

中文 | English

gev is a lightweight, fast non-blocking TCP network library / websocket server based on Reactor mode.

Support custom protocols to quickly and easily build high-performance servers.

Features

  • High-performance event loop based on epoll and kqueue
  • Support multi-core and multi-threading
  • Dynamic expansion of read and write buffers implemented by Ring Buffer
  • Asynchronous read and write
  • SO_REUSEPORT port reuse support
  • Automatically clean up idle connections
  • Support WebSocket/Protobuf, custom protocols
  • Support for scheduled tasks, delayed tasks
  • High performance websocket server

Network model

gev uses only a few goroutines, one of them listens for connections and the others (work coroutines) handle read and write events of connected clients. The count of work coroutines is configurable, which is the core number of host CPUs by default.

Performance Test

📈 Test chart

Test environment: Ubuntu18.04 | 4 Virtual CPUs | 4.0 GiB

Throughput Test

limit GOMAXPROCS=1(Single thread),1 work goroutine

image

limit GOMAXPROCS=4,4 work goroutine

image

Other Test

Speed ​​Test

Compared with the simple performance of similar libraries, the pressure measurement method is the same as the evio project.

  • gnet
  • eviop
  • evio
  • net (StdLib)

limit GOMAXPROCS=1,1 work goroutine

image

limit GOMAXPROCS=1,4 work goroutine

image

limit GOMAXPROCS=4,4 work goroutine

image

Install

go get -u github.com/Allenxuxu/gev

Getting start

echo demo

package main

import (
	"flag"
	"net/http"
	_ "net/http/pprof"
	"strconv"
	"time"

	"github.com/Allenxuxu/gev"
	"github.com/Allenxuxu/gev/log"
	"github.com/Allenxuxu/toolkit/sync/atomic"
)

type example struct {
	Count atomic.Int64
}

func (s *example) OnConnect(c *gev.Connection) {
	s.Count.Add(1)
	//log.Println(" OnConnect : ", c.PeerAddr())
}
func (s *example) OnMessage(c *gev.Connection, ctx interface{}, data []byte) (out interface{}) {
	//log.Println("OnMessage")
	out = data
	return
}

func (s *example) OnClose(c *gev.Connection) {
	s.Count.Add(-1)
	//log.Println("OnClose")
}

func main() {
	go func() {
		if err := http.ListenAndServe(":6060", nil); err != nil {
			panic(err)
		}
	}()

	handler := new(example)
	var port int
	var loops int

	flag.IntVar(&port, "port", 1833, "server port")
	flag.IntVar(&loops, "loops", -1, "num loops")
	flag.Parse()

	s, err := gev.NewServer(handler,
		gev.Network("tcp"),
		gev.Address(":"+strconv.Itoa(port)),
		gev.NumLoops(loops),
		gev.MetricsServer("", ":9091"),
	)
	if err != nil {
		panic(err)
	}

	s.RunEvery(time.Second*2, func() {
		log.Info("connections :", handler.Count.Get())
	})

	s.Start()
}

Handler is an interface that programs must implement.

type CallBack interface {
	OnMessage(c *Connection, ctx interface{}, data []byte) interface{}
	OnClose(c *Connection)
}

type Handler interface {
	CallBack
	OnConnect(c *Connection)
}

OnMessage will be called back when a complete data frame arrives.Users can get the data, process the business logic, and return the data that needs to be sent.

When there is data coming, gev does not call back OnMessage immediately, but instead calls back an UnPacket function.Probably the execution logic is as follows:

ctx, receivedData := c.protocol.UnPacket(c, buffer)
for ctx != nil || len(receivedData) != 0 {
	sendData := c.callBack.OnMessage(c, ctx, receivedData)
	if sendData != nil {
		*tmpBuffer = append(*tmpBuffer, c.protocol.Packet(c, sendData)...)
	}

	ctx, receivedData = c.protocol.UnPacket(c, buffer)
}

protocol

The UnPacket function will check whether the data in the ringbuffer is a complete data frame. If it is, the data will be unpacked and return the payload data. If it is not a complete data frame, it will return directly.

The return value of UnPacket (interface{}, []byte) will be passed in as a call to OnMessage ctx interface{}, data []byte and callback.Ctx is designed to pass special information generated when parsing data frames in the UnPacket function (which is required for complex data frame protocols), and data is used to pass payload data.

type Protocol interface {
	UnPacket(c *Connection, buffer *ringbuffer.RingBuffer) (interface{}, []byte)
	Packet(c *Connection, data interface{}) []byte
}

type DefaultProtocol struct{}

func (d *DefaultProtocol) UnPacket(c *Connection, buffer *ringbuffer.RingBuffer) (interface{}, []byte) {
	s, e := buffer.PeekAll()
	if len(e) > 0 {
		size := len(s) + len(e)
		userBuffer := *c.UserBuffer()
		if size > cap(userBuffer) {
			userBuffer = make([]byte, size)
			*c.UserBuffer() = userBuffer
		}

		copy(userBuffer, s)
		copy(userBuffer[len(s):], e)

		return nil, userBuffer
	} else {
		buffer.RetrieveAll()

		return nil, s
	}
}

func (d *DefaultProtocol) Packet(c *Connection, data interface{}) []byte {
	return data.([]byte)
}

As above, gev provides a default Protocol implementation that will fetch all data in the receive buffer ( ringbuffer ).In actual use, there is usually a data frame protocol of its own, and gev can be set in the form of a plug-in: it is set by variable parameters when creating Server.

s, err := gev.NewServer(handler,gev.Protocol(&ExampleProtocol{}))

Check out the example Protocol for a detailed.

There is also a Send method that can be used for sending data. But Send puts the data to Event-Loop and invokes it to send the data rather than sending data by itself immediately.

Check out the example Server timing push for a detailed.

func (c *Connection) Send(data interface{}, opts ...ConnectionOption) error

ShutdownWrite works for reverting connected status to false and closing connection.

Check out the example Maximum connections for a detailed.

func (c *Connection) ShutdownWrite() error

RingBuffer is a dynamical expansion implementation of circular buffer.

WebSocket

The WebSocket protocol is built on top of the TCP protocol, so gev doesn't need to be built in, but instead provides support in the form of plugins, in the plugins/websocket directory.

code
type Protocol struct {
	upgrade *ws.Upgrader
}

func New(u *ws.Upgrader) *Protocol {
	return &Protocol{upgrade: u}
}

func (p *Protocol) UnPacket(c *connection.Connection, buffer *ringbuffer.RingBuffer) (ctx interface{}, out []byte) {
	upgraded := c.Context()
	if upgraded == nil {
		var err error
		out, _, err = p.upgrade.Upgrade(buffer)
		if err != nil {
			log.Println("Websocket Upgrade :", err)
			return
		}
		c.SetContext(true)
	} else {
		header, err := ws.VirtualReadHeader(buffer)
		if err != nil {
			log.Println(err)
			return
		}
		if buffer.VirtualLength() >= int(header.Length) {
			buffer.VirtualFlush()

			payload := make([]byte, int(header.Length))
			_, _ = buffer.Read(payload)

			if header.Masked {
				ws.Cipher(payload, header.Mask, 0)
			}

			ctx = &header
			out = payload
		} else {
			buffer.VirtualRevert()
		}
	}
	return
}

func (p *Protocol) Packet(c *connection.Connection, data []byte) []byte {
	return data
}

The detailed implementation can be viewed by the plugin. The source code can be viewed using the websocket example.

Example

Buy me a coffee

Paypal: Paypal/AllenXuxu

Thanks

Thanks JetBrains for the free open source license

References

gev's People

Contributors

allenxuxu avatar comolli avatar cs-charles avatar liukun avatar powerexploit avatar qiangmzsx avatar rfyiamcool avatar srdgame avatar wubbalubbaaa avatar xuxiangyang avatar zbh255 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gev's Issues

关于EventLoop.QueueInLoop的一个疑问

EventLoop.QueueInLoop的作用是什么?为什么新的connection需要加入到taskQueueW中后调用poll.Wake(),然后再doPendingFunc中回调将新的connection加入到epoll中。为什么不直接将connection直接加入epoll中,原因是什么?谢谢

windows的支持

不期望在windows上有什么好的性能,但是对于演示或者小连接量的系统,还是有作用的

或许可以像evio一样基于std库实现,接口统一就好

在 k8s 上 pod cpu 满载

一、问题描述

我在两套 k8s 环境都有出现,我分配多少CPU他运行一辆天后就占用多少CPU。在没有什么用户量的情况下就会出现占满。

1、运行环境

# 运行系统
alpine:3.11
# gev 版本
github.com/Allenxuxu/gev v0.1.9
# golang 版本
golang:1.15

2、部分编排文件

        livenessProbe:
          tcpSocket:
            port: 6061
          periodSeconds: 3
          initialDelaySeconds: 3
        resources:
          requests:
            cpu: 1
            memory: 2Gi
          limits:
            cpu: 1
            memory: 2Gi

3、没有新的连接,仅有一些连接和断开的日志,基本都是这种日志,大概隔几秒会有一组

[server_gev_handler.go:25::dm-im/im-gateway/server.(*gevHandler).OnConnect()] Mar 16 09:34:38.953 [D] OnConnect remote address is 172.31.19.178:11738
[server_gev_handler.go:25::dm-im/im-gateway/server.(*gevHandler).OnConnect()] Mar 16 09:34:38.954 [D] OnConnect remote address is 172.31.28.106:46826
[server_gev_handler.go:156::dm-im/im-gateway/server.(*gevHandler).OnClose()] Mar 16 09:34:38.954 [D] OnClose remote address is 172.31.19.178:11738
[server_gev_handler.go:156::dm-im/im-gateway/server.(*gevHandler).OnClose()] Mar 16 09:34:38.954 [D] OnClose remote address is 172.31.28.106:46826

4、pod CPU 信息(当前分配1000M,测试了分配2000M也会满)

im-gateway-857f97b9f-ld2kt             993m         16Mi

5、连接占满的情况下的连接信息

/data # netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn
     19 ESTABLISHED
      8 CLOSE_WAIT
      4 LISTEN
      1 established)
      1 Foreign

6、go profile 信息

File: app
Build ID: 053ed7dab2cea4aa2ece55e7125b24b2e6c1a958
Type: cpu
Time: Mar 16, 2021 at 9:21am (CST)
Duration: 30.23s, Total samples = 29.46s (97.47%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 24990ms, 84.83% of 29460ms total
Dropped 74 nodes (cum <= 147.30ms)
Showing top 10 nodes out of 28
      flat  flat%   sum%        cum   cum%
   14110ms 47.90% 47.90%    15080ms 51.19%  syscall.Syscall6
    4710ms 15.99% 63.88%     4950ms 16.80%  time.now
    1260ms  4.28% 68.16%     2660ms  9.03%  runtime.mapaccess2
    1190ms  4.04% 72.20%     9050ms 30.72%  github.com/Allenxuxu/gev/eventloop.(*EventLoop).handlerEvent
     770ms  2.61% 74.81%     1680ms  5.70%  github.com/Allenxuxu/gev/connection.(*Connection).HandleEvent
     680ms  2.31% 77.12%     1330ms  4.51%  github.com/Allenxuxu/gev/eventloop.(*EventLoop).doPendingFunc
     630ms  2.14% 79.26%      630ms  2.14%  github.com/Allenxuxu/toolkit/sync/spinlock.(*SpinLock).Lock
     630ms  2.14% 81.40%      690ms  2.34%  runtime.(*itabTableType).find
     510ms  1.73% 83.13%    24690ms 83.81%  github.com/Allenxuxu/gev/poller.(*Poller).Poll
     500ms  1.70% 84.83%      500ms  1.70%  sync.(*entry).load

二、期望 CPU 随业务量增减

调用Connection.Send时,必须创建新的buffer,开销略大

`func (c *Connection) Send(buffer []byte) error {
if !c.connected.Get() {
return ErrConnectionClosed
}

c.loop.QueueInLoop(func() {
	if c.connected.Get() {
		c.sendInLoop(c.protocol.Packet(c, buffer))
	}
})
return nil

}`
buffer参数需要等到sendInLoop执行完成后,才能被重用或释放
但目前接口设计上来说,Send调用方是无法知道buffer是否已经被使用完毕,导致无法使用Connection的UserBuffer或者buffer池等方法重复使用buffer,而是每次发送必须创建一个新的buffer,使用完毕后等待gc自动回收,效率略差

暂时想到两个方案:

  1. Connection.Callback增加OnSendInLoopFinish回调,将buffer传参出去,调用方在回调中将buffer重新放回内存池
  2. 修改Protocal的Packet方法的data参数,类型改为interface,允许外部传入protobuf等未打包的对象,在自定义Packet实现时,使用Connection的UserBuffer作为打包buffer

不知道是否可以支持 ?

(CRT-A0001) Shadow of builtin detected

Description

It is not recommended to overshadow builtins. <!--more--> A shadowed variable or function is declared in the scope with the same name as a built-in function. It is not recommended because identifiers shadowing a built-in make it inaccessible to use. ## Examples ### Bad practice go len := 10

Occurrences

There are 2 occurrences of this issue in the repository.

See all occurrences on DeepSource → deepsource.io/gh/Allenxuxu/gev/issue/CRT-A0001/occurrences/

Splice

Is net.Conn available to be used for Splice or is Splice already a function for this project?

sa *unix.Sockaddr 中 unix 包下的结构体不存在 ,具体进入查看详情

有关这个包下的 golang.org/x/sys/unix sa *unix.Sockaddr

func New(fd int, loop *eventloop.EventLoop, sa *unix.Sockaddr, protocol Protocol, tw *timingwheel.TimingWheel, idleTime time.Duration, readCb ReadCallback, closeCb CloseCallback) *Connection {
	conn := &Connection{
		fd:            fd,
		peerAddr:      sockaddrToString(sa),
		outBuffer:     pool.Get(),
		inBuffer:      pool.Get(),
		readCallback:  readCb,
		closeCallback: closeCb,
		loop:          loop,
		idleTime:      idleTime,
		timingWheel:   tw,
		protocol:      protocol,
	}
	conn.connected.Set(true)

	if conn.idleTime > 0 {
		_ = conn.activeTime.Swap(int(time.Now().Unix()))
		conn.timingWheel.AfterFunc(conn.idleTime, conn.closeTimeoutConn())
	}

	return conn
}

包路径 : github.com/Allenxuxu/gev/connection

sa *unix.Sockaddr 不存在

参考 https://github.com/golang/sys 下的 unix 库

异步的优势

go 使用协程+阻塞的模式来处理并发问题。这样的模式虽然对运行时要求很高,但对程序员却非常友好。这样的代码码也非常容易维护。

异步模式最大的问题就是回调嵌套,项目大了根本没法维护。我就是不想用回调方式写业务代码才转 go 的。

你认为这类 go 语言的异步框架有什么优势,要解决什么问题?

Connection.ShutdownWrite()

调用了Connection.ShutdownWrite()怎么不能断开链接,调用后客户端依然可以发送数据,服务端也能收到数据。

cant download

Hello. i want to download this module. but i cant.

$ go get -u -v github.com/Allenxuxu/gev
github.com/Allenxuxu/gev/connection
github.com/Allenxuxu/gev/connection
/home/m_parsa/go/pkg/mod/github.com/!allenxuxu/[email protected]/connection/connection.go:65:31: cannot use int(time.Now().Unix()) (type int) as type int64 in argument to conn.activeTime.Swap
/home/m_parsa/go/pkg/mod/github.com/!allenxuxu/[email protected]/connection/connection.go:137:28: cannot use int(time.Now().Unix()) (type int) as type int64 in argument to c.activeTime.Swap
$ go version
go version go1.16 linux/amd64

Please help me.
thanks

存在err被覆盖导致无法触发的bug

case err == nil && headerSeen != headerSeenAll:

由于在遍历请求头时,触发对请求头Connection校验的错误,之后触发onHost或者onHeader方法,但是由于onHost或者onHeader方法返回nil,导致原本err=ErrHandshakeBadConnection被nil覆盖。建议用临时变量接收onHost和onHeader返回的错误,只有错误不为空才赋值给err变量。
if onHost := u.OnHost; onHost != nil {
if e := onHost(c, v);e != nil; {
err = e
}
}

if onHeader := u.OnHeader; onHeader != nil {
if e := onHeader(c, k, v);e != nil {
err = e
}
}

下载报错呀

D:>go get -u github.com/Allenxuxu/gev
# github.com/Allenxuxu/gev/eventloop
Golang\src\github.com\Allenxuxu\gev\eventloop\eventloop.go:19:11: undefined: poller.Poller
# github.com/Allenxuxu/gev/listener
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:15:38: undefined: unix.Sockaddr
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:47:11: undefined: unix.SetNonblock
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:60:19: undefined: unix.Accept
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:62:14: undefined: unix.EAGAIN
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:67:13: undefined: unix.SetNonblock
Golang\src\github.com\Allenxuxu\gev\listener\listener.go:68:8: undefined: unix.Close

报错 socket operation on non-socket

系统:
CentOS Linux release 7.7.1908 (Core)

复现:

将example/echo的代码里的 out = data 改为 c.Send(data)

然后执行

go run echo.go

tcpkali --workers 8 -c 200 -T 30s -em "\x70" 127.0.0.1:1833

// HandleEvent 内部使用,供 event loop 回调处理事件
func (l *Listener) HandleEvent(fd int, events poller.Event) {
	if events&poller.EventRead != 0 {
		nfd, sa, err := unix.Accept(fd)
		if err != nil {
			if err != unix.EAGAIN {
				log.Error("accept:", err)
			}
			return
		}
		if err := unix.SetNonblock(nfd, true); err != nil {
			_ = unix.Close(nfd)
			log.Error("set nonblock:", err)
			return
		}

		l.handleC(nfd, sa)
	}
}

将会执行 log.Error("accept:", err) 的代码 err是 socket operation on non-socket或 bad file descriptor

客户端发出主动关闭请求时无法触发onClose

在案例控制服务端的最大连接数代码中,通过OnConnect事件和onClose事件来控制链接计数器。经本人测试后,如果是客户端发送header.OpCode为ws.OpClose后,服务端会调用c.ShutdownWrite()方法来关闭连接,同时设置c.connected.Set(false)。而onClose方法在响应关闭连接的事件回调后会触发如下逻辑,由于c.connected在前面已经被设置成了false,所以并不会执行onClose()方法。导致服务端的连接计数器不准确。

func (c *Connection) handleClose(fd int) {
	if c.connected.Get() {
		c.connected.Set(false)
		c.loop.DeleteFdInLoop(fd)

		c.callBack.OnClose(c)
		if err := unix.Close(fd); err != nil {
			log.Error("[close fd]", err)
		}

		ringbuffer.PutInPool(c.inBuffer)
		ringbuffer.PutInPool(c.outBuffer)
	}
}

是否有更详细的文档描述?

首先很开心能分享这样的一个非常不错的框架。但是我还是一个新手,我有一些下面的问题或小小建议:

  1. 文档太糙
  2. 目录结构太随意以及测试*_test.go 正常要放在test目录下更清晰一些,大致看了下目录结构,是否可以这样(实践循环库、网络连接、协议、配置、example、test目录),是否能更简化一些目录?
  3. 本身tcp协议有粘包现象,通讯协议这块是否可以增加自定义预留同时也多开放一些(text、frame、自定义)
  4. 流程是否能清晰一些?或有一个视频讲解?

epoll busy loop

我写了个客户,启动500个协程去连接服务器后立刻关闭连接。发现有时候客户端这边连接已经关闭完了,服务端资源还没释放,就像下面这样:

top - 14:59:36 up 29 days, 18:10,  2 users,  load average: 1.01, 0.57, 0.26
Tasks: 268 total,   1 running, 267 sleeping,   0 stopped,   0 zombie
Cpu0  : 53.9%us, 46.1%sy,  0.0%ni,  0.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu1  :  0.7%us,  0.7%sy,  0.0%ni, 98.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu2  :  2.6%us,  0.3%sy,  0.0%ni, 97.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Cpu3  :  0.3%us,  0.7%sy,  0.0%ni, 99.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   1923764k total,  1737412k used,   186352k free,   191004k buffers
Swap:  4128760k total,    81104k used,  4047656k free,   892780k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 5179 root      20   0  981m 6928 2960 S 100.5  0.4   3:11.67 mirserver
 2531 root      20   0  871m 362m 5408 S  2.6 19.3   1218:10 java
 5244 root      20   0  2704 1208  872 R  1.0  0.1   0:10.24 top

测试用的客户端代码是这样的:

func main(){
    loops, err := strconv.Atoi(os.Args[1])
    if err != nil {
        log.Fatalln(err)
    }
    
    success,failed := 0,0
    
    wg := &sync.WaitGroup{}
    for i:=0;i<loops;i++ {
        go func(){
            wg.Add(1)
            defer wg.Done()
            
            conn, err := net.DialTimeout("tcp", "login.afkplayer.com:7000", time.Second * 60)
            if err != nil {
                failed ++
                log.Println(err)
                return
            }
            success ++
            conn.Close()
        }()
        
    }
    
    wg.Wait()
    log.Println("Test complete...")
    log.Printf("Success: %d Failed: %d\n", success, failed)
}

执行结果是这样的:

E:\HardGo\src>go run main.go 500
2019/12/13 14:57:37 Test complete...
2019/12/13 14:57:37 Success: 500 Failed: 0

Websocket如何實踐廣播跟多播?

Websocket 要怎麼廣播跟多播給客戶端呢?
要自己存一個 map 來使用嗎?

或是底層已經有一個 map 可以開放出來避免重複儲存?

请教如何使用gev做一个im。

谢谢你分享的项目,最近在用gev和websocket做一个im的时候遇到一个问题,如何设计路由。我参考了你写的websocket例子,

type example struct{
	router map[int64]*connection.Connection // userId -> conn
}

router来维护整个用户和它连接的路由信息。
获取目标用户id,我是放在 OnMessage(c *connection.Connection, data []byte) 中第二个参数data中带进来的,就是说data数据也要结构化的,不知道这样做有没有问题啊?
另一个问题,OnClose(c *connection.Connection) 关闭一个连接的时候,只有conn,那如何在router里面清除这个连接归属用户的信息啊?还有用的网页在线websocket客户端断开连接的时候,好像没有触发OnClose,不知道是不是我改的有问题?
谢谢。

gnet 和 gev 的压测结果不一致问题

gev,gnet都很优秀,感谢你们开源付出。但是目前看你两出的基准测试结果还是有差异的,这样让使用者比较困惑,建议你两能否商量一下拿出一个统一的基准测试。

关于拆包

在关于拆解包的时候,unpacket函数接收的buf 依旧是需要按自己的协议来弄吧,还没有像netty 中的,有多个函数来支持分割那种吧(如按位、按分割等)

feature: 是否可以加入读写超时的配置

我现在基于gev基础上抽象了connection结构,基于定时器加回调conn.Close()实现的读超时。建立连接后,加入一个读超时时间,如果无法在一个时间窗口内收到请求,则关闭连接。

timer.AferRun(conn.Close())

是否可以在gev内部加入该功能?

server的主loop相关问题

注意到Listener被单独放置在server.loop里形成一个只包含一个fd的event-loop,这样比起将Listener放置在单独协程里阻塞的Accept并派发TCP连接到各个子event-loop里的方式有什么优势,仅仅是为了保证纯异步非阻塞吗:laughing:,请教。

error

mac SDK1.4和1.5都试过。

../../pkg/mod/github.com/!allenxuxu/[email protected]/connection/connection.go:65:31: cannot use int(time.Now().Unix()) (type int) as type int64 in argument to conn.activeTime.Swap
../../pkg/mod/github.com/!allenxuxu/[email protected]/connection/connection.go:137:28: cannot use int(time.Now().Unix()) (type int) as type int64 in argument to c.activeTime.Swap

关于sendInLoop中EAGAIN的处理

这里当err == unix.EAGAIN时直接return的话,不管n是多少,未Write()成功的那部分数据相当于直接丢弃了,为什么不把这部分未成功写入的数据存到c.outBuffer里再return呢?请教。

n, err := unix.Write(c.fd, data)
if err != nil {
if err == unix.EAGAIN {
return
}
c.handleClose(c.fd)
return

gev在代理的场景如何应用?

onConnect的连接是客户端连接,每个客户端连接成功注册为一个downSession,同时希望他进行一系列逻辑处理以后,在每一个downSession里创建一个上行的连接,作为代理转发。这个
C++的libevent库可以很容易处理这种场景,刚接触gev,不是很清楚,如何处理这种场景

centos6.5 32位系统下 RussellLuo/timingwheel 运行后 panic

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x804a6bc]

goroutine 1 [running]:
runtime/internal/atomic.Xchg64(0xa1201ac, 0xc9fda7b8, 0x16d, 0x0, 0x38b)
/usr/lib/golang/src/runtime/internal/atomic/asm_386.s:151 +0xc
github.com/Allenxuxu/timingwheel%2eV2.(*bucket).SetExpiration(...)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/bucket.go:74
github.com/Allenxuxu/timingwheel%2eV2.(*TimingWheel).add(0xa0ae0a0, 0xa0ade80, 0xa0ae0a0)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/timingwheel.go:79 +0x167
github.com/Allenxuxu/timingwheel%2eV2.(*TimingWheel).add(0xa0ae000, 0xa0ade80, 0xa0adea0)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/timingwheel.go:106 +0x1f2
github.com/Allenxuxu/timingwheel%2eV2.(*TimingWheel).addOrRun(0xa0ae000, 0xa0ade80)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/timingwheel.go:113 +0x29
github.com/Allenxuxu/timingwheel%2eV2.(*TimingWheel).EveryFunc(0xa0ae000, 0x3b9aca00, 0x0, 0xa0782c0, 0xa07c3a0)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/timingwheel.go:185 +0x190
github.com/Allenxuxu/gev.(*Server).RunEvery(...)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/server.go:84
main.(*Server).Start(0xa07c3a0)
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/example/pushmessage/main.go:33 +0x72
main.main()
/home/gohome/pkg/mod/github.com/!allenxuxu/[email protected]/example/pushmessage/main.go:90 +0x85
exit status 2

系统信息:
Linux develop 2.6.32-358.el6.i686 #1 SMP Thu Feb 21 21:50:49 UTC 2013 i686 i686 i386 GNU/Linux

关于doPendingFunc执行的疑问

感谢你开源的高性能通信框架啊,谢谢。
在看源码的时候,我的理解是doPendingFunc函数的执行是通过eventfd来触发的,这样可以统一通过epollWait来调度执行,但有两个疑问:
1,为什么要统一通过epollWait来统一调度呢,不可以想写数据的时候直接就往connfd中write呢?这样会有什么问题呢?谢谢。
2,

func (l *EventLoop) doPendingFunc() {
	l.mu.Lock()
	pf := l.pendingFunc
	l.pendingFunc = nil
	l.mu.Unlock()

	length := len(pf)
	for i := 0; i < length; i++ {  // 遍历所有的pengdingFunc,并执行
		pf[i]()
	}
}

doPendingFunc 这个函数会遍历所有的pendingFunc并执行,那么只要一个pendingFunc写入的eventfd被调度了,那么就会执行所有的pendingFunc,而且定时器也会定时触发这个调度。这样感觉会导致一些异步任务的执行并不是由这个任务本身去触发的,而是由其他的任务触发的,而且还会有多次write(eventfd)后,多次read(eventfd)没必要的情况(因为pengdingFunc已经全部执行完了)。 请问是我哪里理解错了吗?谢谢。

Server.RunEvery: 参数传递错误

当前 Server.RunEvery 的实现:

func (s *Server) RunEvery(d time.Duration, f func()) *timingwheel.Timer {
	return s.timingWheel.ScheduleFunc(&everyScheduler{Interval: time.Second}, f)
}

结构体 everyScheduler 中的 Interval 字段应该使用 d 😃

websocket http header保存

websocket升级的时候需要把http header保存下来的,里面的数据后面conn处理请求要用到的,现在看起来没法做到,需要修改框架支持,

Originally posted by @lyfunny in #4 (comment)

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.