美文网首页
Golang 基于Consul的WebClient

Golang 基于Consul的WebClient

作者: EasyNetCN | 来源:发表于2021-11-03 15:22 被阅读0次

在Spring Cloud中有非常方便好用的WebClient,下面的例子,实现了类似的go版本WebClient(基于Consul的服务注册发现),并且支持处理自定义异常信息

WebClient

package core

import (
    "bytes"
    "encoding/json"
    "errors"
    "fmt"
    "io"
    "math/rand"
    "net/http"
    "net/url"
    "strconv"
    "strings"

    "github.com/hashicorp/consul/api"
)

type WebClient struct {
    HttpClient  *http.Client
    ConsulCient *api.Client
}

func (m *WebClient) Get(service string, path string, urlValues url.Values) (int, []byte, error) {
    return m.Do("GET", "application/json;charset=UTF-8", service, path, urlValues, nil)
}

func (m *WebClient) Post(service string, path string, bodyValue interface{}) (int, []byte, error) {
    return m.Do("POST", "application/json;charset=UTF-8", service, path, nil, bodyValue)
}

func (m *WebClient) Put(service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
    return m.Do("PUT", "application/json;charset=UTF-8", service, path, urlValues, bodyValue)
}

func (m *WebClient) Delete(service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
    return m.Do("DELETE", "application/json;charset=UTF-8", service, path, urlValues, bodyValue)
}

func (m *WebClient) Do(method string, contentType string, service string, path string, urlValues url.Values, bodyValue interface{}) (int, []byte, error) {
    serviceEntry, err := m.serviceEntry(service)

    if err != nil {
        return 0, nil, err
    }

    bodyValueBytes, err1 := json.Marshal(bodyValue)

    if err1 != nil {
        return 0, nil, err1
    }

    if req, err := http.NewRequest(method, m.url(serviceEntry, path, urlValues), bytes.NewReader(bodyValueBytes)); err != nil {
        return 0, nil, err
    } else {
        return m.doRequest(req, contentType)
    }
}

func (m *WebClient) serviceEntry(service string) (*api.ServiceEntry, error) {
    serviceEntries, _, err := m.ConsulCient.Health().Service(service, "", true, nil)

    if err != nil {
        return nil, err
    }

    if len(serviceEntries) == 0 {
        return nil, errors.New("no service to connect")
    }

    return serviceEntries[rand.Intn(len(serviceEntries))], nil
}

func (m *WebClient) url(serviceEntry *api.ServiceEntry, path string, urlValues url.Values) string {
    scheme := "http"

    if meta, ok := serviceEntry.Service.Meta["secure"]; ok {
        if secure, err := strconv.ParseBool(meta); err == nil && secure {
            scheme = "https"
        }
    }

    url := &url.URL{
        Scheme: scheme,
        Host:   fmt.Sprintf("%s:%d", serviceEntry.Service.Address, serviceEntry.Service.Port),
        Path:   path,
    }

    sb := new(strings.Builder)

    sb.WriteString(url.String())

    if len(urlValues) > 0 {
        sb.WriteString("?")
        sb.WriteString(urlValues.Encode())
    }

    return sb.String()
}

func (m *WebClient) doRequest(req *http.Request, contentType string) (int, []byte, error) {
    req.Header.Set("Content-Type", contentType)

    if res, err := m.HttpClient.Do(req); err != nil {
        return res.StatusCode, nil, err
    } else {
        if resBytes, err := io.ReadAll(res.Body); err != nil {
            return res.StatusCode, nil, err
        } else {
            return res.StatusCode, resBytes, nil
        }
    }
}

WebClient Test

package core

import (
    "encoding/json"
    "fmt"
    "net/http"
    "testing"

    "github.com/hashicorp/consul/api"
)

func Test_Get(t *testing.T) {
    config := api.DefaultConfig()

    config.Address = "http://localhost:8500"

    consulClient, _ := api.NewClient(config)
    httpClient := &http.Client{}

    webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}

    if statusCode, resBytes, err := webClient.Get("user-service", "/users/1", nil); err != nil {
        fmt.Println(err)
    } else if statusCode != 200 {
        businessErr := new(BusinessException)

        json.Unmarshal(resBytes, &businessErr)

        fmt.Println(businessErr)
    } else {
        result := make(map[string]interface{})

        json.Unmarshal(resBytes, &result)

        fmt.Print(result)
    }
}

func Test_Post(t *testing.T) {
    config := api.DefaultConfig()

    config.Address = "http://localhost:8500"

    consulClient, _ := api.NewClient(config)
    httpClient := &http.Client{}

    webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}

    searchParam := &PageParam{PageIndex: 1, PageSize: 50}

    if statusCode, resBytes, err := webClient.Post("app-service", "/page-configs/search/page", searchParam); err != nil {
        fmt.Print(err)
    } else if statusCode != 200 {
        businessErr := new(BusinessException)

        json.Unmarshal(resBytes, &businessErr)

        fmt.Println(businessErr)
    } else {
        result := make(map[string]interface{})

        json.Unmarshal(resBytes, &result)

        fmt.Println(result)
    }
}

func Test_BussinessException(t *testing.T) {
    config := api.DefaultConfig()

    config.Address = "http://localhost:8500"

    consulClient, _ := api.NewClient(config)
    httpClient := &http.Client{}

    webClient := &WebClient{HttpClient: httpClient, ConsulCient: consulClient}

    if statusCode, resBytes, err := webClient.Post("user-service", "/users", nil); err != nil {
        fmt.Println(err)
    } else if statusCode != 200 {
        businessErr := new(BusinessException)

        json.Unmarshal(resBytes, &businessErr)

        fmt.Println(businessErr)
    } else {
        result := make(map[string]interface{})

        json.Unmarshal(resBytes, &result)

        fmt.Print(result)
    }
}

BusinessException

package core

type BusinessException struct {
    Status  int    `json:"status"`
    Code    string `json:"code"`
    Message string `json:"message"`
    Trace   string `json:"trace"`
}

func (e *BusinessException) Error() string {
    return e.Trace
}

相关文章

网友评论

      本文标题:Golang 基于Consul的WebClient

      本文链接:https://www.haomeiwen.com/subject/xnqczltx.html