golang http connection multiplexing method

  • 2020-07-21 08:30:52
  • OfStack

server end

golang httpserver turns on the keepalive connection reuse option by default

The handler function needs to read the body data in its entirety to construct the return message, otherwise the connection reuse will fail when the data cannot be sent once.

The sample is as follows


package main
 
import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)
 
func connHandler(w http.ResponseWriter, r *http.Request) {
	// parse
	r.ParseForm()
	response_time := r.Header.Get("sleep-time")
	// <= NOTE
	if _, err := ioutil.ReadAll(r.Body); err != nil {
		http.Error(w, err.Error(), 500)
		return
	}
	defer r.Body.Close()
	// sleep for some time
	resp_time := 1
	if response_time != "" {
		ms, _ := strconv.ParseFloat(response_time, 64)
		resp_time = (int)(ms * 1000)
	}
	time.Sleep(time.Duration(resp_time) * time.Millisecond)
	// parepare response
	status := 200
	body := ""
	w.Header().Set("Content-Type", "text/plain")
	w.Header().Set("Content-Length", strconv.Itoa(len(body)))
	w.WriteHeader(status)
	w.Write([]byte(body))
}
 
func main() {
	http.HandleFunc("/", connHandler)
	if err := http.ListenAndServe(":server_port", nil); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

client end

The client needs to build global client, read response body in its entirety, and close body


package main

import (
  "bytes"
  "fmt"
  "io"
  "io/ioutil"
  "log"
  "net/http"
  "time"
)

var (
  httpClient *http.Client
)

const (
  MaxIdleConnections int = 20
  RequestTimeout   int = 30
)

// init HTTPClient
func init() {
  httpClient = createHTTPClient()
}

// createHTTPClient for connection re-use
func createHTTPClient() *http.Client {
  client := &http.Client{
   Transport: &http.Transport{
      MaxIdle  ConnsPerHost: MaxIdleConnections,
 },
 Timeout: time.Duration(RequestTimeout) * time.Second,
  }
  return client
}

func conn_reuse_post(conn_reuse_times int) {
  var endPoint string = "http://server_ip:server_port/"
  data := []byte{}
  // fill data 
  for i := 0; i < conn_reuse_times; i++ {
 // use global httpClient to send request
 resp, err := httpClient.Post(endPoint, "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(data)))
 fmt.Println(resp)
 if err != nil {
   log.Println("err", err)
   return
 }
 io.Copy(ioutil.Discard, resp.Body) // <= NOTE
 resp.Body.Close()  // <= NOTE
  }
}

func main() {
  conn_reuse_post(5)
}

Related articles: