рдЧреЛ, рд╕рдВрд╕реНрдХрд░рдг 2 рдкрд░ рд▓реЛрдб рдкрд░реАрдХреНрд╖рдг

рдЧреЛ-рдореАрдЯрд░ рдХреЛ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрдиреЗ рдХреЗ рд╣рд╛рде рдирд╣реАрдВ рдкрд╣реБрдВрдЪреЗред рдЙрддреНрдкрд╛рджрдХрддрд╛ рдмрдврд╝рд╛рдПрдВ, рдЗрд╕ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдкрд░ рдЕрдзрд┐рдХ рдирд┐рдпрдВрддреНрд░рдг рд╣рд╛рд╕рд┐рд▓ рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ рдкреНрд░рдХреЛрдк рдХреЗ рдХрд░реАрдм рд▓рд╛рдПрдВред рдЖрджрд░реНрд╢ рд░реВрдк рдореЗрдВ, рдореИрдВ рдПрдХ рдЖрд╕рд╛рдиреА рд╕реЗ рдФрд░ рдЖрд╕рд╛рдиреА рд╕реЗ рд╡рд┐рд╕реНрддрд╛рд░ рдпреЛрдЧреНрдп рд╡рд┐рдХрд▓реНрдк рджреЗрдЦрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред рд╣рд╛рдВ, рд╣рд╛рд▓ рд╣реА рдореЗрдВ Lua рд▓рд┐рдкрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдХреБрд▓реНрд╣рд╛рдбрд╝реА рдХрд╛ рд╕рдорд░реНрдерди рд╢реБрд░реВ рд╣реБрдЖ, рдЬреЛ рдХрдИ рдЕрд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЛ рд╣рд▓ рдХрд░рддреА рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╡рд╣рд╛рдВ рднреА рдЕрдкреНрд░рд┐рдп рдмрд╛рд░реАрдХрд┐рдпрд╛рдВ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЖрдк рдЙрдиреНрдирдд рдЖрдВрдХрдбрд╝реЗ рдПрдХрддреНрд░ рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реЛрдВрдЧреЗ, рдХреНрдпреЛрдВрдХрд┐ рдЖрдВрдХрдбрд╝реЗ рдЖрдЙрдЯрдкреБрдЯ рддрд░реАрдХреЗ рдХреЗрд╡рд▓ рдкрд╣рд▓реА рд╕реНрдЯреНрд░реАрдо рдкрд░ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, рдФрд░ рдЕрдиреНрдп рдкрд╣реБрдВрдЪ рдзрд╛рд░рд╛рдУрдВ рдореЗрдВ рдПрдХрддреНрд░рд┐рдд рдбреЗрдЯрд╛ред рдирд╣реАрдВ, рдЗрд╕рд▓рд┐рдП рдпрд╣ рд╕реНрд░реЛрдд рдХреЛрдб рдХреЛ рд╕рдордЭрдиреЗ рдФрд░ рдЗрд╕реЗ рдЕрдкрдиреЗ рд▓рд┐рдП рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдлрд┐рд░ рд╕реЗ рдиреАрдЪреЗ рдЖрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ рддреБрдЪреНрдЫ рдХрд╛рд░реНрдп рдирд╣реАрдВ рд╣реИред рдФрд░ рдЗрд╕рд▓рд┐рдП, рд╣рдо рдЧреЛ рдкрд░, рдмрдиреНрд╕ рдХреЗ рд╕рд╛рде рдПрдХ рд▓реЛрдб рдкрд░реАрдХреНрд╖рдг рддреИрдпрд╛рд░ рдХрд░ рд░рд╣реЗ рд╣реИрдВред рдмрд┐рд▓реНрд▓реА рдХреЗ рдиреАрдЪреЗ, рдХреМрди рдкрд░рд╡рд╛рд╣ рдХрд░рддрд╛ рд╣реИред

рдХреНрдпрд╛ рд╣реИ рдФрд░ рдХреНрдпрд╛ рдЪрд╛рд╣рд┐рдП

рд╢реБрд░реБрдЖрдд рд╕реЗ рд╣рдо рд╕рдордЭреЗрдВрдЧреЗ рдХрд┐ рд╣рдореЗрдВ рдХреНрдпрд╛ рдЪрд╛рд╣рд┐рдП:
- GET / POST / PUT / DELETE рдЕрдиреБрд░реЛрдз рднреЗрдЬрдирд╛
- URL рдЧрдгрди, рдФрд░ POST рдирд┐рдХрд╛рдп
- рдЦреБрд▓реЗ рдХрдиреЗрдХреНрд╢рди рдкрд░ рдирд┐рдпрдВрддреНрд░рдг
- рдкреНрд░рд╡рд╛рд╣ рдирд┐рдпрдВрддреНрд░рдг
- рдкрд░реАрдХреНрд╖рдг рдХреА рдЕрд╡рдзрд┐ рдХрд╛ рд╕рдВрдХреЗрдд
- рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб рдЕрдзрд┐рдХрддрдо рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рд╕реАрдорд╛
HTTP рд╕рд░реНрд╡рд░ рдХреЛ рдЧрд░реНрдо рдХрд░рдиреЗ рдХреЗ рд╕рдордп рд╡рд┐рдХреГрддрд┐ рд╕реЗ рдмрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдБрдХрдбрд╝реЛрдВ рд╕реЗ рдкрд╣рд▓реЗ рдХреБрдЫ рд╕реЗрдХрдВрдб рдХреЛ рдмрд╛рд╣рд░ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛

рдпреЛрдЬрдирд╛

- рдХрдиреЗрдХреНрд╢рди рдкреВрд▓
- рд╕рд░рд▓ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛
- рдЖрдБрдХрдбрд╝реЗ
- рд▓рд╛рдн
рдЬреЛрд░ рд╕реЗ рд╡рд┐рдЪрд╛рд░ рдХрд░рдирд╛
рдЪреВрдВрдХрд┐ рдЖрдкрдХреЛ рдХрдиреЗрдХреНрд╢рдиреЛрдВ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдорд╛рдирдХ http.Client рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рдирд╣реАрдВ рд╣реИ (рдФрд░ рдпрд╣ рдЗрд╕ рддрд░рд╣ рдХреЗ рдХрд╛рд░реНрдп рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдмрдбрд╝рд╛ рд╣реИ), рдпрд╣ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдЬрд╛рдирддрд╛ рд╣реИ, рдЬрд┐рд╕рдХреЗ рдХрд╛рд░рдг рдкреНрд░рджрд░реНрд╢рди рдкреНрд░рднрд╛рд╡рд┐рдд рд╣реЛрддрд╛ рд╣реИред рдЪреВрдБрдХрд┐ рд╣рдо рдЕрдиреБрд░реЛрдз рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рд╢реНрд░рдорд┐рдХ рд╕реВрддреНрд░ рдХрд╛ рдЕрд░реНрде рд░рдЦрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рдХрдиреЗрдХреНрд╢рди рдХреЗ рдПрдХ рдкреВрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ рдЬрд┐рд╕реЗ рд╡реЗ рдЖрдкрд╕ рдореЗрдВ рд╕рд╛рдЭрд╛ рдХрд░реЗрдВрдЧреЗред рдпрд╣ рд╕рд░реНрд╡рд░ рд╕реЗ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрддрд╛ рд╣реИ, рд╣рдо рд╕рд┐рд░реНрдл рдЗрд╕ рдкрд░ рдореВрд▓реНрдпрд╡рд╛рди рд╕рдордп рдЦреЛ рджреЗрддреЗ рд╣реИрдВред рдЧреБрдЬрд░рдиреЗ рд╡рд╛рд▓реЗ рдЯреНрд░реИрдлрд╝рд┐рдХ рдХрд╛ рдЕрдиреБрдорд╛рди рдХреИрд╕реЗ рд▓рдЧрд╛рдпрд╛ рдЬрд╛рдП? рдорд╛рдирдХ http.Request, http.Respose рдРрд╕реА рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рджрд╛рди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдпрд╣ рдЙрдирдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рд╣рдореЗрдВ рдПрдХ рд╕рд░рд▓ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЬреЛ рд╣рдореЗрдВ рд╡рд╣ рд╕рдм рдХреБрдЫ рдкреНрд░рджрд╛рди рдХрд░реЗрдЧреА рдЬрд┐рд╕рдХреА рд╣рдореЗрдВ рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдХрдЪреНрдЪреЗ рдбреЗрдЯрд╛ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рдирд╛ рдФрд░ рдЗрд╕реЗ рдЕрдВрдд рдореЗрдВ рдПрдХрддреНрд░рд┐рдд рдХрд░рдирд╛ рдХрд╛рдо рдирд╣реАрдВ рдХрд░реЗрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рдореЗрдореЛрд░реА рд░рдмрд░ рдирд╣реАрдВ рд╣реИред рдордХреНрдЦреА рдкрд░ рдПрдХ рдореВрд░реНрддрд┐ рд░рдЦрдирд╛ред


рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ


рд╣рдо рдПрдХ рд╕реАрдорд┐рдд рдЪреИрдирд▓ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдХрдиреЗрдХреНрд╢рди рдкреВрд▓ рд▓рд┐рдЦрддреЗ рд╣реИрдВред рдпрд╣ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдкреВрд▓ рдХреА рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛, рдЙрдиреНрд╣реЛрдВрдиреЗ рдЪреИрдирд▓ рд╕реЗ рдСрдмреНрдЬреЗрдХреНрдЯ рд▓рд┐рдпрд╛, рдХрд╛рдо рдХрд┐рдпрд╛, рдЗрд╕реЗ рд╡рд╛рдкрд╕ рд░рдЦрд╛ред
type Connection struct { conn net.Conn manager *ConnectionManager } type ConnectionManager struct { conns chan *Connection config *Config } func NewConnectionManager(config *Config) (result *ConnectionManager) { result = &ConnectionManager{config: config, conns: make(chan *Connection, config.Connections)} for i := 0; i < config.Connections; i++ { connection := &Connection{manager: result} if connection.Dial() != nil { ConnectionErrors++ } result.conns <- connection } return } func (this *ConnectionManager) Get() *Connection { return <-this.conns } func (this *Connection) Dial() error { if this.IsConnected() { this.Disconnect() } conn, err := net.Dial("tcp4", this.manager.config.Url.Host) if err == nil { this.conn = conn } return err } func (this *Connection) Disconnect() { this.conn.Close() this.conn = nil } func (this *Connection) IsConnected() bool { return this.conn != nil } func (this *Connection) Return() { this.manager.conns <- this } 

рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдпрд╣рд╛рдВ рдЖрдк рдЧреЛ рд╕реНрд░реЛрдд рдХреЛ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ, рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдЗрд╕реЗ рд╡рд╣рд╛рдВ рдХреИрд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдПрдХ рд╕рд░рд▓реАрдХреГрдд рд╕рд╛рджреГрд╢реНрдп рдмрдирд╛ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдореБрдЦреНрдп рдЕрдВрддрд░ рдкреНрд░рддреНрдпреЗрдХ рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдпрд╛рддрд╛рдпрд╛рдд рдХреА рдорд╛рддреНрд░рд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рдореВрд▓реНрдпрд╡рд╛рди рд╕рдордп рдмрдЪрд╛рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рд╣реИред
рдкреНрд░рд╛рд░реНрдердирд╛
 type Request struct { Method string URL *url.URL Header map[string][]string Body io.Reader ContentLength int64 Host string BufferSize int64 } func (req *Request) Write(w io.Writer) error { bw := &bytes.Buffer{} fmt.Fprintf(bw, "%s %s HTTP/1.1\r\n", valueOrDefault(req.Method, "GET"), req.URL.RequestURI()) fmt.Fprintf(bw, "Host: %s\r\n", req.Host) userAgent := "" if req.Header != nil { if ua := req.Header["User-Agent"]; len(ua) > 0 { userAgent = ua[0] } } if userAgent != "" { fmt.Fprintf(bw, "User-Agent: %s\r\n", userAgent) } if req.Method == "POST" || req.Method == "PUT" { fmt.Fprintf(bw, "Content-Length: %d\r\n", req.ContentLength) } if req.Header != nil { for key, values := range req.Header { if key == "User-Agent" || key == "Content-Length" || key == "Host" { continue } for _, value := range values { fmt.Fprintf(bw, "%s: %s\r\n", key, value) } } } io.WriteString(bw, "\r\n") if req.Method == "POST" || req.Method == "PUT" { bodyReader := bufio.NewReader(req.Body) _, err := bodyReader.WriteTo(bw) if err != nil { return err } } req.BufferSize = int64(bw.Len()) _, err := bw.WriteTo(w) return err } 


рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛
 type Response struct { Status string StatusCode int Header map[string][]string ContentLength int64 BufferSize int64 } func ReadResponse(r *bufio.Reader) (*Response, error) { tp := textproto.NewReader(r) resp := &Response{} line, err := tp.ReadLine() if err != nil { return nil, err } f := strings.SplitN(line, " ", 3) resp.BufferSize += int64(len(f) + 2) if len(f) < 2 { return nil, errors.New("Response Header ERROR") } reasonPhrase := "" if len(f) > 2 { reasonPhrase = f[2] } resp.Status = f[1] + " " + reasonPhrase resp.StatusCode, err = strconv.Atoi(f[1]) if err != nil { return nil, errors.New("malformed HTTP status code") } resp.Header = make(map[string][]string) for { line, err := tp.ReadLine() if err != nil { return nil, errors.New("Response Header ERROR") } resp.BufferSize += int64(len(line) + 2) if len(line) == 0 { break } else { f := strings.SplitN(line, ":", 2) resp.Header[f[0]] = append(resp.Header[strings.TrimSpace(f[0])], strings.TrimSpace(f[1])) } } if cl := resp.Header["Content-Length"]; len(cl) > 0 { i, err := strconv.ParseInt(cl[0], 10, 0) if err == nil { resp.ContentLength = i } } buff := make([]byte, resp.ContentLength) r.Read(buff) resp.BufferSize += int64(resp.ContentLength) return resp, nil } 


рдкрд░реАрдХреНрд╖рдг рдХреЗ рд╕рдордп рд╕рдорд╛рдкреНрдд рд╣реЛрдиреЗ рдкрд░ рд╣рдорд╛рд░реЗ рдереНрд░реЗрдбреНрд╕ рдХреЛ рдмрдВрдж рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдо рдкреНрд░рд╡рд╛рд╣ рдХреЗ рдХрд╛рдо рдХреЛ рдкреВрд░рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЪреИрдирд▓ рдмрдирд╛рдПрдВрдЧреЗ рдФрд░ рдПрдХ рдЪреИрдирд▓ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рдереНрд░реЗрдб рд░рд┐рдкреЛрд░реНрдЯ рдХрд░реЗрдЧрд╛ рдХрд┐ рдЗрд╕рдиреЗ рдЕрдкрдирд╛ рдХрд╛рдо рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкреВрд░рд╛ рдХрд┐рдпрд╛ рд╣реИ
 WorkerQuit := make(chan bool, *_threads) WorkerQuited := make(chan bool, *_threads) 

рд╕рдордп, рдФрд░ рд╣рдо Ctr + C (SIGTERM) рдХрд╛ рднреА рдЗрдВрддрдЬрд╛рд░ рдХрд░реЗрдВрдЧреЗ рддрд╛рдХрд┐ рд╣рдорд╛рд░рд╛ рдЖрд╡реЗрджрди рдХрд┐рд╕реА рднреА рд╕рдордп рдкрд░реАрдХреНрд╖рдг рдкреВрд░рд╛ рдХрд░ рд╕рдХреЗ
 //Start Ctr+C listen signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) //Wait timers or SIGTERM select { case <-time.After(config.Duration): case <-signalChan: } for i := 0; i < config.Threads; i++ { config.WorkerQuit <- true } //Wait for threads complete for i := 0; i < config.Threads; i++ { <-config.WorkerQuited } 

рдЕрдм рд╣рдо рдХрд╛рд░реНрдпрдХрд░реНрддрд╛ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓рддреЗ рд╣реИрдВ: рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЛ рд╕реАрдорд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рд╣рдо рдХреБрд▓ рд╕рдВрдЦреНрдпрд╛ рдХрд╛ рдЕрдкрдирд╛ рд╣рд┐рд╕реНрд╕рд╛ рд▓реЗрддреЗ рд╣реИрдВ, рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб 4 рдмрд╛рд░ рд╣рдо рдХрд╛рдЙрдВрдЯрд░ рдХреЛ рдмрдврд╝рд╛рдПрдВрдЧреЗ рдФрд░ рдпрд╛ рддреЛ рдХрдиреЗрдХреНрд╢рди рдХреЛ рдореБрдлреНрдд рдпрд╛ рдХрд╛рдо рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХреА рдкреНрд░рддреАрдХреНрд╖рд╛ рдХрд░реЗрдВрдЧреЗред
 func NewThread(config *Config) { timerAllow := time.NewTicker(time.Duration(250) * time.Millisecond) allow := int32(config.MRQ / 4 / config.Threads) if config.MRQ == -1 { allow = 2147483647 } else if allow <= 0 { allow = 1 } var connectionErrors int32 = 0 currentAllow := allow for { select { //        case <-timerAllow.C: currentAllow = allow //   case connection := <-config.ConnectionManager.conns: currentAllow-- //    -     if currentAllow < 0 { connection.Return() } else { //  req := getRequest(config.Method, config.Url, config.Source.GetNext()) //      if config.Reconnect && connection.IsConnected() { connection.Disconnect() } //  ,     if !connection.IsConnected() { if connection.Dial() != nil { connectionErrors++ } } //    ,    if connection.IsConnected() { go writeSocket(connection, req, config.RequestStats) } else { connection.Return() } } //  case <-config.WorkerQuit: //    atomic.AddInt32(&ConnectionErrors, connectionErrors) //  config.WorkerQuited <- true return } } } 

рдЬреИрд╕реЗ рд╣реА рдХрдиреЗрдХреНрд╢рди рдореБрдлреНрдд рд╣реЛрддрд╛ рд╣реИ, рд╣рдо рдЕрдЧрд▓рд╛ рдЕрдиреБрд░реЛрдз рдмрдирд╛рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдЕрддреБрд▓реНрдпрдХрд╛рд▓рд┐рдХ рд░реВрдк рд╕реЗ рднреЗрдЬрдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЬрдм рддрдХ рд╕рдордп рд╕рдорд╛рдкреНрдд рдирд╣реАрдВ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рддрдм рддрдХ рдПрдХ рд╕рд░реНрдХрд▓ рдореЗрдВред рдЕрдиреБрд░реЛрдз рднреЗрдЬреЗ рдЬрд╛рдиреЗ рдФрд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдХрдиреЗрдХреНрд╢рди рдкреВрд▓ рдореЗрдВ рд╡рд╛рдкрд╕ рдЖ рдЬрд╛рддрд╛ рд╣реИ, рдФрд░ рдереНрд░реЗрдб рдЗрд╕реЗ рдлрд┐рд░ рд╕реЗ рдЙрдард╛рдПрдЧрд╛ред
рдкреНрд░рд╕реНрддреБрдд рдХрд░рдиреЗ рдХрд╛ рдЕрдиреБрд░реЛрдз
 func writeSocket(connection *Connection, req *http.Request, read chan *RequestStats) { result := &RequestStats{} //          defer func() { connection.Return() read <- result }() now := time.Now() conn := connection.conn bw := bufio.NewWriter(conn) //  err := req.Write(bw) if err != nil { result.WriteError = err return } err = bw.Flush() if err != nil { result.WriteError = err return } //  res, err := http.ReadResponse(bufio.NewReader(conn)) if err != nil { result.ReadError = err return } //   result.Duration = time.Now().Sub(now) result.NetOut = req.BufferSize result.NetIn = res.BufferSize result.ResponseCode = res.StatusCode req.Body = nil } 


рд░рд┐рдХреНрд╡реЗрд╕реНрдЯрд╕реНрдЯреИрдЯ рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рд╕реЗ рдЖрдВрдХрдбрд╝реЗ рдПрдХрддреНрд░ рдХрд░рдиреЗ рдФрд░ рдЗрд╕реЗ рд╡реНрдпрд╡рд╕реНрдерд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╕рднреА рдмрд╛рдИрдВ рдУрд░ рдЫреЛрдЯрд╛ рд╣реИ
 //  type StatsSource struct { Readed int64 Writed int64 Requests int Skiped int Min time.Duration Max time.Duration Sum int64 Codes map[int]int DurationPercent map[time.Duration]int ReadErrors int WriteErrors int Work time.Duration } //    type StatsSourcePerSecond struct { Readed int64 Writed int64 Requests int Skiped int Sum int64 } //  func StartStatsAggregator(config *Config) { allowStore := true allowStoreTime := time.After(config.ExcludeSeconds) if config.ExcludeSeconds.Seconds() > 0 { allowStore = false } verboseTimer := time.NewTicker(time.Duration(1) * time.Second) if config.Verbose { fmt.Printf("%s %s %s %s %s %s\n", newSpancesFormatRightf("Second", 10, "%s"), newSpancesFormatRightf("Total", 10, "%s"), newSpancesFormatRightf("Req/sec", 10, "%s"), newSpancesFormatRightf("Avg/sec", 10, "%s"), newSpancesFormatRightf("In/sec", 10, "%s"), newSpancesFormatRightf("Out/sec", 10, "%s"), ) } else { verboseTimer.Stop() } source = StatsSource{ Codes: make(map[int]int), DurationPercent: make(map[time.Duration]int), } perSecond := StatsSourcePerSecond{} start := time.Now() for { select { //    case <-verboseTimer.C: if perSecond.Requests-perSecond.Skiped > 0 && config.Verbose { //    avgMilliseconds := perSecond.Sum / int64(perSecond.Requests-perSecond.Skiped) avg := time.Duration(avgMilliseconds) * time.Millisecond //  fmt.Printf("%s %s %s %s %s %s\n", newSpancesFormatRightf(roundToSecondDuration(time.Now().Sub(start)), 10, "%v"), newSpancesFormatRightf(source.Requests, 10, "%d"), newSpancesFormatRightf(perSecond.Requests, 10, "%d"), newSpancesFormatRightf(avg, 10, "%v"), newSpancesFormatRightf(Bites(perSecond.Readed), 10, "%s"), newSpancesFormatRightf(Bites(perSecond.Writed), 10, "%s"), ) } //  perSecond = StatsSourcePerSecond{} //          case <-allowStoreTime: allowStore = true //    case res := <-config.RequestStats: //   -   ,      if res.ReadError != nil { source.ReadErrors++ continue } else if res.WriteError != nil { source.WriteErrors++ continue } //  source.Requests++ perSecond.Requests++ perSecond.Readed += res.NetIn perSecond.Writed += res.NetOut source.Readed += res.NetIn source.Writed += res.NetOut //      HTTP  source.Codes[res.ResponseCode]++ if !allowStore { perSecond.Skiped++ source.Skiped++ continue } //    sum := int64(res.Duration.Seconds() * 1000) source.Sum += sum perSecond.Sum += sum //     if source.Min > res.Duration { source.Min = roundDuration(res.Duration) } if source.Max < res.Duration { source.Max = roundDuration(res.Duration) } //        10  duration := time.Duration(res.Duration.Nanoseconds()/10000000) * time.Millisecond * 10 source.DurationPercent[duration]++ //   case <-config.StatsQuit: //    source.Work = time.Duration(time.Now().Sub(start).Seconds()*1000) * time.Millisecond if config.Verbose { s := "" for { if len(s) >= 61 { break } s += "-" } fmt.Println(s) } //  config.StatsQuit <- true return } } } 


рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рдХрд╣рдирд╛

рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рддрд░реНрдХ рдФрд░ рдкреНрд░рд╛рд░реВрдк рдЖрдБрдХрдбрд╝реЗ рдЖрдЙрдЯрдкреБрдЯ рдореИрдВ рдЫреЛрдбрд╝ рджреВрдБрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реИред рдЕрдм рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╣рдореЗрдВ рдХреНрдпрд╛ рдорд┐рд▓рд╛ред рдирдореВрдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдо Node.js рдХреНрд▓рд╕реНрдЯрд░ рдкрд░ wrk рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВ
 % ./wrk -c 21 -t 7 -d 30s -L http://localhost:3001/index.html Running 30s test @ http://localhost:3001/index.html 7 threads and 21 connections Thread Stats Avg Stdev Max +/- Stdev Latency 1.09ms 6.55ms 152.07ms 99.63% Req/Sec 5.20k 3.08k 14.33k 58.75% Latency Distribution 50% 490.00us 75% 0.89ms 90% 1.83ms 99% 5.04ms 1031636 requests in 30.00s, 153.48MB read Requests/sec: 34388.25 Transfer/sec: 5.12MB 

рдФрд░ GOMAXPROCS = 1 рдХреЗ рд╕рд╛рде рдЬрд╛рдиреЗ рдкрд░ рдПрдХ рд╣реА рдмрд╛рдд
 % ./go-meter -t 7 -c 21 -d 30s -u http://localhost:3001/index.html Running test threads: 7, connections: 21 in 30s GET http://localhost:3001/index.html Stats: Min Avg Max Latency 0 0 83ms 843183 requests in 30s, net: in 103MB, out 62MB HTTP Codes: 200 100.00% Latency: 0 99.99% 10ms - 80ms 0.01% Requests: 28106.10/sec Net In: 27MBit/sec Net Out: 17MBit/sec Transfer: 5.5MB/sec 

рд╣рдореЗрдВ рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдб 34388 рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рдЦрд┐рд▓рд╛рдл 28106 рдорд┐рд▓рддреЗ рд╣реИрдВ - рдпрд╣ рд╢реБрджреНрдз рд╕реА + рдЗрд╡реЗрдВрдЯ рд▓реВрдк + рдПрдирдЖрдИрдУ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рд▓рдЧрднрдЧ 20% рдХрдо рд╣реИред рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реИ, рдЬрдм GOMAXPROCS рдХреЛ рдмрджрд▓рдирд╛ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рд░реВрдк рд╕реЗ рдХреЛрдИ рдЕрдВрддрд░ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЕрдзрд┐рдХрд╛рдВрд╢ рдкреНрд░реЛрд╕реЗрд╕рд░ рд╕рдордп Node.js. рджреНрд╡рд╛рд░рд╛ рд▓рд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рд╡рд┐рдкрдХреНрд╖:
- 20% рдкреНрд░рджрд░реНрд╢рди рдХрд╛ рдиреБрдХрд╕рд╛рди, рдЖрдк рдЕрдиреБрд░реЛрдз / рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЛ рдЖрд╕рд╛рди рдмрдирд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдереЛрдбрд╝рд╛ рдкреНрд░рджрд░реНрд╢рди рджреЗ рд╕рдХрддреЗ рд╣реИрдВ
- рдЕрднреА рддрдХ HTTPS рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ
- рдЖрдк рдЕрднреА рддрдХ рдХрд╕реНрдЯрдо HTTP рд╣реЗрдбрд░ рдФрд░ рдЯрд╛рдЗрдордЖрдЙрдЯ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ

рдпрд╣рд╛рдБ рд╕рднреА рд╕реНрд░реЛрдд - рдЧрд┐рддреБрдм

рдХреИрд╕реЗ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ
 % go get github.com/a696385/go-meter % $GOPATH/bin/go-meter -h 


рдЖрдкрдХрд╛ рдзреНрдпрд╛рди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!

Source: https://habr.com/ru/post/In203328/


All Articles