connection_cache.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package tcp
  2. import (
  3. "net"
  4. "sync"
  5. "time"
  6. "github.com/v2ray/v2ray-core/common/signal"
  7. )
  8. type AwaitingConnection struct {
  9. conn net.Conn
  10. expire time.Time
  11. }
  12. func (this *AwaitingConnection) Expired() bool {
  13. return this.expire.Before(time.Now())
  14. }
  15. type ConnectionCache struct {
  16. sync.Mutex
  17. cache map[string][]*AwaitingConnection
  18. cleanupOnce signal.Once
  19. }
  20. func NewConnectionCache() *ConnectionCache {
  21. return &ConnectionCache{
  22. cache: make(map[string][]*AwaitingConnection),
  23. }
  24. }
  25. func (this *ConnectionCache) Cleanup() {
  26. defer this.cleanupOnce.Reset()
  27. for len(this.cache) > 0 {
  28. time.Sleep(time.Second * 4)
  29. this.Lock()
  30. for key, value := range this.cache {
  31. size := len(value)
  32. changed := false
  33. for i := 0; i < size; {
  34. if value[i].Expired() {
  35. value[i].conn.Close()
  36. value[i] = value[size-1]
  37. size--
  38. changed = true
  39. } else {
  40. i++
  41. }
  42. }
  43. if changed {
  44. for i := size; i < len(value); i++ {
  45. value[i] = nil
  46. }
  47. value = value[:size]
  48. this.cache[key] = value
  49. }
  50. }
  51. this.Unlock()
  52. }
  53. }
  54. func (this *ConnectionCache) Recycle(dest string, conn net.Conn) {
  55. this.Lock()
  56. defer this.Unlock()
  57. aconn := &AwaitingConnection{
  58. conn: conn,
  59. expire: time.Now().Add(time.Second * 4),
  60. }
  61. var list []*AwaitingConnection
  62. if v, found := this.cache[dest]; found {
  63. v = append(v, aconn)
  64. list = v
  65. } else {
  66. list = []*AwaitingConnection{aconn}
  67. }
  68. this.cache[dest] = list
  69. go this.cleanupOnce.Do(this.Cleanup)
  70. }
  71. func FindFirstValid(list []*AwaitingConnection) int {
  72. for idx, conn := range list {
  73. if !conn.Expired() {
  74. return idx
  75. }
  76. go conn.conn.Close()
  77. }
  78. return -1
  79. }
  80. func (this *ConnectionCache) Get(dest string) net.Conn {
  81. this.Lock()
  82. defer this.Unlock()
  83. list, found := this.cache[dest]
  84. if !found {
  85. return nil
  86. }
  87. firstValid := FindFirstValid(list)
  88. if firstValid == -1 {
  89. delete(this.cache, dest)
  90. return nil
  91. }
  92. res := list[firstValid].conn
  93. list = list[firstValid+1:]
  94. this.cache[dest] = list
  95. return res
  96. }