1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-01-17 14:57:44 -05:00

optimize udp hub

This commit is contained in:
Darien Raymond 2016-11-18 21:30:03 +01:00
parent 4ee758c4d2
commit a14795e1e6
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
6 changed files with 120 additions and 30 deletions

View File

@ -62,6 +62,9 @@ func (this *FileLogWriter) Log(log LogEntry) {
} }
func (this *FileLogWriter) run() { func (this *FileLogWriter) run() {
this.cancel.WaitThread()
defer this.cancel.FinishThread()
for { for {
entry, open := <-this.queue entry, open := <-this.queue
if !open { if !open {
@ -69,12 +72,11 @@ func (this *FileLogWriter) run() {
} }
this.logger.Print(entry + platform.LineSeparator()) this.logger.Print(entry + platform.LineSeparator())
} }
this.cancel.Done()
} }
func (this *FileLogWriter) Close() { func (this *FileLogWriter) Close() {
close(this.queue) close(this.queue)
<-this.cancel.WaitForDone() this.cancel.WaitForDone()
this.file.Close() this.file.Close()
} }

View File

@ -1,35 +1,51 @@
package signal package signal
import (
"sync"
)
// CancelSignal is a signal passed to goroutine, in order to cancel the goroutine on demand. // CancelSignal is a signal passed to goroutine, in order to cancel the goroutine on demand.
type CancelSignal struct { type CancelSignal struct {
cancel chan struct{} cancel chan struct{}
done chan struct{} done sync.WaitGroup
} }
// NewCloseSignal creates a new CancelSignal. // NewCloseSignal creates a new CancelSignal.
func NewCloseSignal() *CancelSignal { func NewCloseSignal() *CancelSignal {
return &CancelSignal{ return &CancelSignal{
cancel: make(chan struct{}), cancel: make(chan struct{}),
done: make(chan struct{}),
} }
} }
func (this *CancelSignal) WaitThread() {
this.done.Add(1)
}
// Cancel signals the goroutine to stop. // Cancel signals the goroutine to stop.
func (this *CancelSignal) Cancel() { func (this *CancelSignal) Cancel() {
close(this.cancel) close(this.cancel)
} }
func (this *CancelSignal) Cancelled() bool {
select {
case <-this.cancel:
return true
default:
return false
}
}
// WaitForCancel should be monitored by the goroutine for when to stop. // WaitForCancel should be monitored by the goroutine for when to stop.
func (this *CancelSignal) WaitForCancel() <-chan struct{} { func (this *CancelSignal) WaitForCancel() <-chan struct{} {
return this.cancel return this.cancel
} }
// Done signals the caller that the goroutine has completely finished. // FinishThread signals that current goroutine has finished.
func (this *CancelSignal) Done() { func (this *CancelSignal) FinishThread() {
close(this.done) this.done.Done()
} }
// WaitForDone is used by caller to wait for the goroutine finishes. // WaitForDone is used by caller to wait for the goroutine finishes.
func (this *CancelSignal) WaitForDone() <-chan struct{} { func (this *CancelSignal) WaitForDone() {
return this.done this.done.Wait()
} }

View File

@ -95,6 +95,7 @@ func (this *DokodemoDoor) ListenUDP() error {
this.meta.Address, this.meta.Port, udp.ListenOption{ this.meta.Address, this.meta.Port, udp.ListenOption{
Callback: this.handleUDPPackets, Callback: this.handleUDPPackets,
ReceiveOriginalDest: this.config.FollowRedirect, ReceiveOriginalDest: this.config.FollowRedirect,
Concurrency: 2,
}) })
if err != nil { if err != nil {
log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err) log.Error("Dokodemo failed to listen on ", this.meta.Address, ":", this.meta.Port, ": ", err)

View File

@ -59,7 +59,7 @@ func (this *TimedUserValidator) Release() {
} }
this.cancel.Cancel() this.cancel.Cancel()
<-this.cancel.WaitForDone() this.cancel.WaitForDone()
this.Lock() this.Lock()
defer this.Unlock() defer this.Unlock()
@ -100,7 +100,9 @@ func (this *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx
} }
func (this *TimedUserValidator) updateUserHash(interval time.Duration) { func (this *TimedUserValidator) updateUserHash(interval time.Duration) {
L: this.cancel.WaitThread()
defer this.cancel.FinishThread()
for { for {
select { select {
case now := <-time.After(interval): case now := <-time.After(interval):
@ -109,10 +111,9 @@ L:
this.generateNewHashes(nowSec, entry.userIdx, entry) this.generateNewHashes(nowSec, entry.userIdx, entry)
} }
case <-this.cancel.WaitForCancel(): case <-this.cancel.WaitForCancel():
break L return
} }
} }
this.cancel.Done()
} }
func (this *TimedUserValidator) Add(user *protocol.User) error { func (this *TimedUserValidator) Add(user *protocol.User) error {

View File

@ -58,7 +58,7 @@ func NewListener(address v2net.Address, port v2net.Port, options internet.Listen
l.tlsConfig = securitySettings.GetTLSConfig() l.tlsConfig = securitySettings.GetTLSConfig()
} }
} }
hub, err := udp.ListenUDP(address, port, udp.ListenOption{Callback: l.OnReceive}) hub, err := udp.ListenUDP(address, port, udp.ListenOption{Callback: l.OnReceive, Concurrency: 2})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -5,28 +5,95 @@ import (
"sync" "sync"
"v2ray.com/core/common/alloc" "v2ray.com/core/common/alloc"
"v2ray.com/core/common/dice"
"v2ray.com/core/common/log" "v2ray.com/core/common/log"
v2net "v2ray.com/core/common/net" v2net "v2ray.com/core/common/net"
"v2ray.com/core/common/signal"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
"v2ray.com/core/transport/internet/internal" "v2ray.com/core/transport/internet/internal"
) )
type UDPPayload struct {
payload *alloc.Buffer
session *proxy.SessionInfo
}
type UDPPayloadHandler func(*alloc.Buffer, *proxy.SessionInfo) type UDPPayloadHandler func(*alloc.Buffer, *proxy.SessionInfo)
type UDPHub struct { type UDPPayloadQueue struct {
sync.RWMutex queue []chan UDPPayload
conn *net.UDPConn callback UDPPayloadHandler
option ListenOption cancel *signal.CancelSignal
accepting bool }
pool *alloc.BufferPool
func NewUDPPayloadQueue(option ListenOption) *UDPPayloadQueue {
queue := &UDPPayloadQueue{
callback: option.Callback,
cancel: signal.NewCloseSignal(),
queue: make([]chan UDPPayload, option.Concurrency),
}
for i := range queue.queue {
queue.queue[i] = make(chan UDPPayload, 64)
go queue.Dequeue(queue.queue[i])
}
return queue
}
func (this *UDPPayloadQueue) Enqueue(payload UDPPayload) {
size := len(this.queue)
for i := 0; i < size; i++ {
idx := 0
if size > 1 {
idx = dice.Roll(size)
}
select {
case this.queue[idx] <- payload:
return
default:
}
}
}
func (this *UDPPayloadQueue) Dequeue(queue <-chan UDPPayload) {
this.cancel.WaitThread()
defer this.cancel.FinishThread()
for !this.cancel.Cancelled() {
payload, open := <-queue
if !open {
return
}
this.callback(payload.payload, payload.session)
}
}
func (this *UDPPayloadQueue) Close() {
this.cancel.Cancel()
for _, queue := range this.queue {
close(queue)
}
this.cancel.WaitForDone()
} }
type ListenOption struct { type ListenOption struct {
Callback UDPPayloadHandler Callback UDPPayloadHandler
ReceiveOriginalDest bool ReceiveOriginalDest bool
Concurrency int
}
type UDPHub struct {
sync.RWMutex
conn *net.UDPConn
pool *alloc.BufferPool
cancel *signal.CancelSignal
queue *UDPPayloadQueue
option ListenOption
} }
func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UDPHub, error) { func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UDPHub, error) {
if option.Concurrency < 1 {
option.Concurrency = 1
}
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{ udpConn, err := net.ListenUDP("udp", &net.UDPAddr{
IP: address.IP(), IP: address.IP(),
Port: int(port), Port: int(port),
@ -48,8 +115,10 @@ func ListenUDP(address v2net.Address, port v2net.Port, option ListenOption) (*UD
} }
hub := &UDPHub{ hub := &UDPHub{
conn: udpConn, conn: udpConn,
option: option,
pool: alloc.NewBufferPool(2048, 64), pool: alloc.NewBufferPool(2048, 64),
queue: NewUDPPayloadQueue(option),
option: option,
cancel: signal.NewCloseSignal(),
} }
go hub.start() go hub.start()
return hub, nil return hub, nil
@ -59,8 +128,10 @@ func (this *UDPHub) Close() {
this.Lock() this.Lock()
defer this.Unlock() defer this.Unlock()
this.accepting = false this.cancel.Cancel()
this.conn.Close() this.conn.Close()
this.cancel.WaitForDone()
this.queue.Close()
} }
func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error) { func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error) {
@ -71,9 +142,8 @@ func (this *UDPHub) WriteTo(payload []byte, dest v2net.Destination) (int, error)
} }
func (this *UDPHub) start() { func (this *UDPHub) start() {
this.Lock() this.cancel.WaitThread()
this.accepting = true defer this.cancel.FinishThread()
this.Unlock()
oobBytes := make([]byte, 256) oobBytes := make([]byte, 256)
for this.Running() { for this.Running() {
@ -91,15 +161,15 @@ func (this *UDPHub) start() {
if this.option.ReceiveOriginalDest && noob > 0 { if this.option.ReceiveOriginalDest && noob > 0 {
session.Destination = RetrieveOriginalDest(oobBytes[:noob]) session.Destination = RetrieveOriginalDest(oobBytes[:noob])
} }
go this.option.Callback(buffer, session) this.queue.Enqueue(UDPPayload{
payload: buffer,
session: session,
})
} }
} }
func (this *UDPHub) Running() bool { func (this *UDPHub) Running() bool {
this.RLock() return !this.cancel.Cancelled()
defer this.RUnlock()
return this.accepting
} }
// Connection return the net.Conn underneath this hub. // Connection return the net.Conn underneath this hub.