Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PortPool: add check port before alloc #72

Merged
merged 1 commit into from
Oct 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 81 additions & 23 deletions shell/ports.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,49 +27,107 @@ SOFTWARE.
*/
package main

import "fmt"
import (
"fmt"
"net"
"strconv"
)

// The port pool manage available ports.
// PortPool The port pool manage available ports.
type PortPool struct {
shell *ShellBoss
// alloc new port from ports, fill ports from left,
// release to ports when free port.
// Alloc new port from ports. Alloc new port from the head of the list.
// Release to ports when free port.
// If check failed, put the port to the tail of the list
ports []int
left []int
// The ports in use. fill used when alloc
used []int
}

// alloc port in [start,stop]
// NewPortPool alloc port in [start,stop]
func NewPortPool(start, stop int) *PortPool {
v := &PortPool{}
v.ports = make([]int, stop-start+1)
for i := start; i <= stop; i++ {
if len(v.ports) < 64 {
v.ports = append(v.ports, i)
} else {
v.left = append(v.left, i)
}
v.ports[i-start] = i
}
return v
}

func (v *PortPool) Alloc(nbPort int) (ports []int, err error) {
// allocOnePort alloc a port from the port pool
func (v *PortPool) allocOnePort() (int, error) {
if len(v.ports) <= 0 {
return 0, fmt.Errorf("empty port pool")
}

for i, p := range v.ports {
if checkPort(p) {
// delete i-index element
v.ports = append(v.ports[:i], v.ports[i+1:]...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个地方可以改成:

  1. 取一个port出来,checkPort。从ports里面删除这个port。
  2. 如果port不可用,就添加到末尾。
  3. 如果可用,就返回。这样就不用used这个了。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 used 变量可以方便增加一个方法, 打印出我当前在使用哪些 port.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我增加了一个方法 GetPortsInUse() 可以获取当前使用中的 port

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为什么需要暴露这个函数呢:GetPortsInUse,这个是给谁用的?

v.used = append(v.used, p)
return p, nil
}
}

return 0, fmt.Errorf("No available port")
}

// Alloc alloc nbPort ports from the port pool
func (v *PortPool) Alloc(nbPort int) ([]int, error) {
if nbPort <= 0 {
return nil, fmt.Errorf("invalid ports %v", nbPort)
}
if len(v.ports)+len(v.left) < nbPort {
return nil, fmt.Errorf("no %v port available, left %v", nbPort, len(v.ports)+len(v.left))
}

if len(v.ports) < nbPort {
cp := nbPort - len(v.ports)
v.ports = append(v.ports, v.left[0:cp]...)
v.left = v.left[cp:]
return nil, fmt.Errorf("no %v port available, left %v", nbPort, len(v.ports))
}

ports = v.ports[0:nbPort]
v.ports = v.ports[nbPort:]
return
ports := []int{}
for i := 0; i < nbPort; i++ {
p, err := v.allocOnePort()
if err != nil {
return nil, err
} else {
ports = append(ports, p)
}
}
return ports, nil
}

// Free free port from the port pool
func (v *PortPool) Free(port int) {
v.ports = append(v.ports, port)
// Free used ports to the head of the ports list for better port reused.
v.ports = append([]int{port}, v.ports...)
for i, p := range v.used {
if port == p {
// delete i-index element
v.used = append(v.used[:i], v.used[i+1:]...)
return
}
}
}

// GetPortsInUse get a list of port in use
func (v *PortPool) GetPortsInUse() []int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为何要export这个函数?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

另外,case 有failed:https://circleci.com/gh/ossrs/go-oryx/16

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一般的一个程序会占用一个端口, 启动时会配置进去, nc这个项目会占用多个端口, 而且是动态分配的, 需要提供个接口来查询哪些端口被占用了. 这样能了解当前系统占用了哪些端口.

return v.used
}

// checkPort check if a port is available
func checkPort(port int) bool {
// Concatenate a colon and the port
host := ":" + strconv.Itoa(port)

// Try to create a server with the port
listener, err := net.Listen("tcp", host)

// if it fails then the port is likely taken
if err != nil {
return false
}

// close the server
listener.Close()

// we successfully used and closed the port
// so it's now available to be used again
return true
}
38 changes: 20 additions & 18 deletions shell/shell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,55 +24,57 @@ SOFTWARE.

package main

import "testing"
import (
"testing"
)

func TestPortPool_Alloc(t *testing.T) {
p := NewPortPool(1, 10)
p := NewPortPool(6001, 6010)

if _, err := p.Alloc(0); err == nil {
t.Error("should error")
}

if ps, err := p.Alloc(1); err != nil {
t.Errorf("alloc failed, err is %v", err)
} else if len(ps) != 1 || ps[0] != 1 {
t.Errorf("alloc failed, ports=%v", ps)
t.Errorf("alloc 1 failed, err is %v", err)
} else if len(ps) != 1 || ps[0] != 6001 {
t.Errorf("alloc 1 failed, ports=%v", ps)
} else if ps, err = p.Alloc(9); err != nil {
t.Errorf("alloc failed, err is %v", err)
} else if len(ps) != 9 || ps[0] != 2 || ps[8] != 10 {
t.Errorf("alloc failed, ports=%v", ps)
t.Errorf("alloc 9 failed, err is %v", err)
} else if len(ps) != 9 || ps[0] != 6002 || ps[8] != 6010 {
t.Errorf("alloc 9 failed, ports=%v", ps)
} else if ps, err = p.Alloc(1); err == nil {
t.Errorf("should error, ports=%v", ps)
}
}

func TestPortPool_Free(t *testing.T) {
p := NewPortPool(1, 10)
p := NewPortPool(6001, 6010)

if ps, err := p.Alloc(10); err != nil || len(ps) != 10 || ps[0] != 1 || ps[9] != 10 {
if ps, err := p.Alloc(10); err != nil || len(ps) != 10 || ps[0] != 6001 || ps[9] != 6010 {
t.Errorf("alloc failed, ports=%v, err is %v", ps, err)
}
p.Free(11)
if ps, err := p.Alloc(1); err != nil || len(ps) != 1 || ps[0] != 11 {
p.Free(6010)
if ps, err := p.Alloc(1); err != nil || len(ps) != 1 {
t.Error("free failed, ports=%v, err is %v", ps, err)
} else if ps[0] != 11 {
} else if ps[0] != 6010 {
t.Errorf("invalid port=%v", ps)
}
}

func TestPortPool_Alloc2(t *testing.T) {
p := NewPortPool(1, 100)
p := NewPortPool(9001, 9100)

if ps, err := p.Alloc(64); err != nil || len(ps) != 64 || ps[0] != 1 || ps[63] != 64 {
if ps, err := p.Alloc(64); err != nil || len(ps) != 64 || ps[0] != 9001 || ps[63] != 9064 {
t.Errorf("alloc failed, ports=%v, err is %v", ps, err)
} else if ps, err := p.Alloc(36); err != nil || len(ps) != 36 || ps[0] != 65 || ps[35] != 100 {
} else if ps, err := p.Alloc(36); err != nil || len(ps) != 36 || ps[0] != 9065 || ps[35] != 9100 {
t.Errorf("alloc failed, ports=%v, err is %v", ps, err)
} else if ps, err = p.Alloc(1); err == nil {
t.Errorf("should error, ports=%v", ps)
}

p.Free(11)
if ps, err := p.Alloc(1); err != nil || len(ps) != 1 || ps[0] != 11 {
p.Free(9011)
if ps, err := p.Alloc(1); err != nil || len(ps) != 1 || ps[0] != 9011 {
t.Errorf("alloc failed, ports=%v, err is %v", ps, err)
} else if ps, err = p.Alloc(1); err == nil {
t.Errorf("should error, ports=%v", ps)
Expand Down