Add ConnectionsMax function that limits connections per pid.

The goal is to improve performance of connection fetching connections across
all processes when some processes can have several hundred or thousands of file
descriptors. Right now when you have many thousands of fds the process spends
lots of time inside the syscalls from Readdir and Readlink.

The public API works as before with two new functions:

- `ConnectionsMax`
- `ConnectionsPidMax`

Each function takes an additional int argument that sets the max number of fds
read per process.
pull/268/head
Conor Branagan 9 years ago
parent 449c8250b0
commit 198e65c801

@ -292,6 +292,12 @@ func Connections(kind string) ([]ConnectionStat, error) {
return ConnectionsPid(kind, 0) return ConnectionsPid(kind, 0)
} }
// Return a list of network connections opened returning at most `max`
// connections for each running process.
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
return ConnectionsPidMax(kind, 0, max)
}
// Return a list of network connections opened by a process. // Return a list of network connections opened by a process.
func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) { func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind] tmap, ok := netConnectionKindMap[kind]
@ -302,9 +308,33 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
var err error var err error
var inodes map[string][]inodeMap var inodes map[string][]inodeMap
if pid == 0 { if pid == 0 {
inodes, err = getProcInodesAll(root) inodes, err = getProcInodesAll(root, 0)
} else {
inodes, err = getProcInodes(root, pid, 0)
if len(inodes) == 0 {
// no connection for the pid
return []ConnectionStat{}, nil
}
}
if err != nil {
return nil, fmt.Errorf("cound not get pid(s), %d", pid)
}
return statsFromInodes(root, pid, tmap, inodes)
}
// Return up to `max` network connections opened by a process.
func ConnectionsPidMax(kind string, pid int32, max int) ([]ConnectionStat, error) {
tmap, ok := netConnectionKindMap[kind]
if !ok {
return nil, fmt.Errorf("invalid kind, %s", kind)
}
root := common.HostProc()
var err error
var inodes map[string][]inodeMap
if pid == 0 {
inodes, err = getProcInodesAll(root, max)
} else { } else {
inodes, err = getProcInodes(root, pid) inodes, err = getProcInodes(root, pid, max)
if len(inodes) == 0 { if len(inodes) == 0 {
// no connection for the pid // no connection for the pid
return []ConnectionStat{}, nil return []ConnectionStat{}, nil
@ -313,10 +343,14 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("cound not get pid(s), %d", pid) return nil, fmt.Errorf("cound not get pid(s), %d", pid)
} }
return statsFromInodes(root, pid, tmap, inodes)
}
func statsFromInodes(root string, pid int32, tmap []netConnectionKindType, inodes map[string][]inodeMap) ([]ConnectionStat, error) {
dupCheckMap := make(map[string]bool) dupCheckMap := make(map[string]bool)
var ret []ConnectionStat var ret []ConnectionStat
var err error
for _, t := range tmap { for _, t := range tmap {
var path string var path string
var ls []connTmp var ls []connTmp
@ -368,11 +402,15 @@ func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
} }
// getProcInodes returnes fd of the pid. // getProcInodes returnes fd of the pid.
func getProcInodes(root string, pid int32) (map[string][]inodeMap, error) { func getProcInodes(root string, pid int32, max int) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap) ret := make(map[string][]inodeMap)
dir := fmt.Sprintf("%s/%d/fd", root, pid) dir := fmt.Sprintf("%s/%d/fd", root, pid)
files, err := ioutil.ReadDir(dir) f, err := os.Open(dir)
if err != nil {
return ret, nil
}
files, err := f.Readdir(max)
if err != nil { if err != nil {
return ret, nil return ret, nil
} }
@ -484,7 +522,7 @@ func (p *process) fillFromStatus() error {
return nil return nil
} }
func getProcInodesAll(root string) (map[string][]inodeMap, error) { func getProcInodesAll(root string, max int) (map[string][]inodeMap, error) {
pids, err := Pids() pids, err := Pids()
if err != nil { if err != nil {
return nil, err return nil, err
@ -492,7 +530,7 @@ func getProcInodesAll(root string) (map[string][]inodeMap, error) {
ret := make(map[string][]inodeMap) ret := make(map[string][]inodeMap)
for _, pid := range pids { for _, pid := range pids {
t, err := getProcInodes(root, pid) t, err := getProcInodes(root, pid, max)
if err != nil { if err != nil {
return ret, err return ret, err
} }

@ -15,11 +15,32 @@ func TestGetProcInodesAll(t *testing.T) {
} }
root := common.HostProc("") root := common.HostProc("")
v, err := getProcInodesAll(root) v, err := getProcInodesAll(root, 0)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotEmpty(t, v) assert.NotEmpty(t, v)
} }
func TestConnectionsMax(t *testing.T) {
if os.Getenv("CIRCLECI") == "true" {
t.Skip("Skip CI")
}
max := 10
v, err := ConnectionsMax("tcp", max)
assert.Nil(t, err)
assert.NotEmpty(t, v)
cxByPid := map[int32]int{}
for _, cx := range v {
if cx.Pid > 0 {
cxByPid[cx.Pid]++
}
}
for _, c := range cxByPid {
assert.True(t, c <= max)
}
}
type AddrTest struct { type AddrTest struct {
IP string IP string
Port int Port int

Loading…
Cancel
Save