Skip to content

Instantly share code, notes, and snippets.

@krishnakumar4a4
Created September 5, 2019 16:03
Show Gist options
  • Save krishnakumar4a4/4601cdb7c9cd84664fac6eeb8fe92012 to your computer and use it in GitHub Desktop.
Save krishnakumar4a4/4601cdb7c9cd84664fac6eeb8fe92012 to your computer and use it in GitHub Desktop.
TCP level read and write stats using standard golang HTTP client
package main
import (
"context"
"fmt"
"net"
"net/http"
"time"
)
// ConnStats stats for tcp flow
type ConnStats struct {
ReadBytes int64
ReadTime time.Duration
WroteBytes int64
WroteTime time.Duration
Closed bool
}
// ConnProfiler profiler for connection
type ConnProfiler struct {
StatsChan chan ConnStats
Conn net.Conn
}
func (cp ConnProfiler) Read(b []byte) (n int, err error) {
fmt.Println("reading")
t1 := time.Now()
n, err = cp.Conn.Read(b)
d := time.Now().Sub(t1)
cs := ConnStats{
ReadBytes: int64(n),
ReadTime: d,
}
cp.StatsChan <- cs
return
}
func (cp ConnProfiler) Write(b []byte) (n int, err error) {
fmt.Println("writing")
t1 := time.Now()
n, err = cp.Conn.Write(b)
d := time.Now().Sub(t1)
cs := ConnStats{
WroteBytes: int64(n),
WroteTime: d,
}
cp.StatsChan <- cs
return
}
// Close closes the connection
func (cp ConnProfiler) Close() error {
fmt.Println("closing")
cs := ConnStats{
Closed: true,
}
cp.StatsChan <- cs
return cp.Conn.Close()
}
// LocalAddr local address
func (cp ConnProfiler) LocalAddr() net.Addr {
return cp.Conn.LocalAddr()
}
// RemoteAddr remote address
func (cp ConnProfiler) RemoteAddr() net.Addr {
return cp.Conn.RemoteAddr()
}
// SetDeadline sets dead line
func (cp ConnProfiler) SetDeadline(t time.Time) error {
return cp.Conn.SetDeadline(t)
}
// SetReadDeadline sets read dead line
func (cp ConnProfiler) SetReadDeadline(t time.Time) error {
return cp.Conn.SetReadDeadline(t)
}
// SetWriteDeadline sets write dead line
func (cp ConnProfiler) SetWriteDeadline(t time.Time) error {
return cp.Conn.SetWriteDeadline(t)
}
func main() {
go req()
// sample server code
mux := http.NewServeMux()
mux.HandleFunc("/test", func(rw http.ResponseWriter, req *http.Request) {
fmt.Println("received request and sleep")
time.Sleep(time.Second * 10)
fmt.Println("ended sleep")
})
s := http.Server{
Addr: ":3301",
Handler: mux,
}
s.ListenAndServe()
}
func req() {
// client code
statsChan := make(chan ConnStats, 10)
stats := ConnProfiler{
StatsChan: statsChan,
}
// Client requests
go func() {
for {
c := http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := net.Dial(network, addr)
if err != nil {
return nil, err
}
stats.Conn = conn
return stats, nil
},
},
}
resp, err := c.Get("http://localhost:3301/test")
if err != nil {
fmt.Println("could not send request, err:", err.Error())
}
resp.Body.Close()
time.Sleep(time.Second * 2)
}
}()
// Accumulator for connection stats
go func() {
totStats := ConnStats{}
for {
statsData := <-statsChan
if statsData.Closed {
break
}
totStats.ReadBytes += statsData.ReadBytes
totStats.ReadTime += statsData.ReadTime
totStats.WroteBytes += statsData.WroteBytes
totStats.WroteTime += statsData.WroteTime
// fmt.Println("statsData: ", statsData)
fmt.Println("totStats: ", totStats)
}
fmt.Println("totStats: ", totStats)
}()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment