1.依赖包
import "github.com/dghubble/sling"
2.功能介绍
项目开发中,发送http请求的场景,推荐使用Sling库。Sling本身是基于net/http来处理发送请求,同时做了较好的封装,既可以利用net/http的一些特性(如:httptrace),同时又不必关心net/http库的一些琐碎细节。Sling对http请求的要素method、baseUrl、Path、query、body、request、response等做了封装,基本使用可以参考https://github.com/dghubble/sling 上的示例代码。
type Sling struct {
// http Client for doing requests
httpClient Doer
// HTTP method (GET, POST, etc.)
method string
// raw url string for requests
rawURL string
// stores key-values pairs to add to request's Headers
header http.Header
// url tagged query structs
queryStructs []interface{}
// body provider
bodyProvider BodyProvider
// response decoder
responseDecoder ResponseDecoder
}
// Request returns a new http.Request created with the Sling properties.
// Returns any errors parsing the rawURL, encoding query structs, encoding
// the body, or creating the http.Request.
func (s *Sling) Request() (*http.Request, error) {
reqURL, err := url.Parse(s.rawURL)
if err != nil {
return nil, err
}
err = addQueryStructs(reqURL, s.queryStructs)
if err != nil {
return nil, err
}
var body io.Reader
if s.bodyProvider != nil {
body, err = s.bodyProvider.Body()
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(s.method, reqURL.String(), body)
if err != nil {
return nil, err
}
addHeaders(req, s.header)
return req, err
}
api_path转换为url
3.使用方法
package httputil
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"sync"
"time"
"github.com/dghubble/sling"
"github.com/emicklei/go-restful"
)
const (
jsonAcceptHeader = "application/json"
testAcceptHeader = "text/plain"
clientNumsPerHost = 30
)
type HttpPoolMgr struct {
maxConns int
cacheClient map[string]*HttpPool
}
var httpPoolMgr *HttpPoolMgr
func (this *HttpPoolMgr) Borrow(host string) (*http.Client, error) {
if httpPool, ok := this.cacheClient[host]; ok {
return httpPool.Borrow()
} else {
httpPool := newHttpPool()
httpPool.host = host
this.cacheClient[host] = httpPool
return httpPool.Borrow()
}
}
func (this *HttpPoolMgr) Return(host string, c *http.Client) {
if httpPool, ok := this.cacheClient[host]; ok {
httpPool.Return(c)
return
} else {
httpPool := newHttpPool()
httpPool.host = host
this.cacheClient[host] = httpPool
httpPool.Return(c)
return
}
}
//var httpPool *HttpPool
type HttpPool struct {
host string
pool chan *http.Client
used int
l *sync.Mutex
}
func newHttpPool() *HttpPool {
httpPool := &HttpPool{}
httpPool.pool = make(chan *http.Client, clientNumsPerHost)
httpPool.l = &sync.Mutex{}
return httpPool
}
func (this *HttpPool) Return(c *http.Client) {
this.used--
if len(this.pool) < clientNumsPerHost {
this.pool <- c
}
}
func (this *HttpPool) Borrow() (*http.Client, error) {
if client := this.innerBorrow(); client != nil {
return client, nil
}
i := 1
for {
i++
time.Sleep(500 * time.Millisecond)
if client := this.innerBorrow(); client != nil {
return client, nil
}
//池中无
if i > 5 {
return nil, errors.New(fmt.Sprintf("Can not get conn to [%s] from cache.", this.host))
}
}
}
func (this *HttpPool) innerBorrow() *http.Client {
select {
case c := <-this.pool:
this.used++
return c
default:
this.l.Lock()
defer this.l.Unlock()
if this.used < clientNumsPerHost {
this.used++
return &http.Client{}
}
}
return nil
}
func InitHttpTool() {
http.DefaultTransport.(*http.Transport).MaxIdleConns = 20000
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 1000
http.DefaultTransport.(*http.Transport).DisableKeepAlives = false
http.DefaultTransport.(*http.Transport).IdleConnTimeout = 2 * time.Minute
httpPoolMgr = &HttpPoolMgr{}
httpPoolMgr.cacheClient = make(map[string]*HttpPool)
}
func Request(method string, api_path string, model interface{}, headers map[string]string) (int, []byte, error) {
_sling := sling.New()
if strings.ToLower(method) == "post" {
_sling = _sling.Post(api_path)
// create path and map variables
//path := "/api/users/"
//
//_sling = _sling.Path(path)
// body params
_sling = _sling.BodyJSON(model)
} else if strings.ToLower(method) == "get" {
_sling = _sling.Get(api_path)
} else if strings.ToLower(method) == "put" {
_sling = _sling.Put(api_path)
if model != nil && model != "" {
_sling = _sling.BodyJSON(model)
}
} else if strings.ToLower(method) == "head" {
_sling = _sling.Head(api_path)
} else if strings.ToLower(method) == "delete" {
_sling = _sling.Delete(api_path)
}
if headers == nil {
headers = make(map[string]string)
}
headers["Content-Type"] = jsonAcceptHeader
httpStatusCode, body, err := request(_sling, headers)
return httpStatusCode, body, err
}
func HttpRequest(method string, api_path string, model interface{}, req *restful.Request) (int, []byte, error) {
authorization := req.HeaderParameter("Authorization")
return Request(method, api_path, model, map[string]string{"Authorization": authorization})
}
func HttpRequsetText(method string, api_path string, text io.Reader, headers map[string]string) (int, []byte, error) {
_sling := sling.New()
if strings.ToLower(method) == "post" {
_sling := _sling.Post(api_path)
// create path and map variables
//path := "/api/users/"
//
//_sling = _sling.Path(path)
// body params
_sling = _sling.Body(text)
}
if headers == nil {
headers = make(map[string]string)
}
headers["Content-Type"] = jsonAcceptHeader
httpStatusCode, body, err := request(_sling, headers)
return httpStatusCode, body, err
}
func request(_sling *sling.Sling, headers map[string]string) (int, []byte, error) {
for k, v := range headers {
_sling = _sling.Set(k, v)
}
req, err := _sling.Request()
if err != nil {
return 400, nil, err
}
host := req.Host
if host == "" {
return 500, nil, errors.New("the req's host cannot be empty.")
}
client, err := httpPoolMgr.Borrow(host)
if err != nil {
//panic(err)
return 500, nil, err
}
defer httpPoolMgr.Return(host, client)
resp, err := client.Do(req)
if err != nil {
return 500, nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
return resp.StatusCode, body, err
}
if 500 == resp.StatusCode {
log.Println(errors.New(string(body)))
return resp.StatusCode, body, errors.New(string(body))
}
return resp.StatusCode, body, err
}
func PostRequest(url string, model interface{}, headers map[string]string) (status int, respBytes []byte, err error) {
status, respBytes, err = Request("POST", url, model, headers)
return
}
func GetRequest(url string, model interface{}, headers map[string]string) (status int, respBytes []byte, err error) {
status, respBytes, err = Request("GET", url, model, headers)
return
}
type TLSConfig struct {
CAFile string
CertFile string
KeyFile string
ServerName string
InsecureSkipVerify bool
}
func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify}
if len(cfg.CAFile) > 0 {
caCertPool := x509.NewCertPool()
caCert, err := ioutil.ReadFile(cfg.CAFile)
if err != nil {
return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CAFile, err)
}
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.RootCAs = caCertPool
}
if len(cfg.ServerName) > 0 {
tlsConfig.ServerName = cfg.ServerName
}
if len(cfg.CertFile) > 0 && len(cfg.KeyFile) == 0 {
return nil, fmt.Errorf("client cert file %q specified without client key file", cfg.CertFile)
} else if len(cfg.KeyFile) > 0 && len(cfg.CertFile) == 0 {
return nil, fmt.Errorf("client key file %q specified without client cert file", cfg.KeyFile)
} else if len(cfg.CertFile) > 0 && len(cfg.KeyFile) > 0 {
cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile)
if err != nil {
return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.CertFile, cfg.KeyFile, err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}