rpc/rpc.go

393 lines
9.9 KiB
Go

// Package rpc ...
//
// Description : rpc ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022-06-29 15:21
package rpc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"sync"
"time"
"github.com/tidwall/gjson"
"git.zhangdeman.cn/zhangdeman/util"
"github.com/ddliu/go-httpclient"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
var (
// Request 请求实例
Request *request
)
// InitRPC 初始化RPC服务
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:23 2022/6/29
func InitRPC(serviceTable map[string]*Service, loggerInstance *zap.Logger) error {
if nil == serviceTable {
serviceTable = make(map[string]*Service)
}
for _, item := range serviceTable {
if item.ApiTable == nil {
item.ApiTable = make(map[string]*Api)
}
}
Request = &request{
logger: loggerInstance,
serviceTable: serviceTable,
lock: &sync.RWMutex{},
}
return nil
}
type request struct {
logger *zap.Logger
serviceTable map[string]*Service
lock *sync.RWMutex
}
// AddService 新增一个服务
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:13 2022/6/29
func (r *request) AddService(serviceInfo *Service) error {
if nil == serviceInfo {
return errors.New("service info is nil")
}
if nil == serviceInfo.SuccessCodeList {
serviceInfo.SuccessCodeList = []string{"0"}
}
if nil == serviceInfo.SuccessHttpCodeList {
serviceInfo.SuccessHttpCodeList = []int{http.StatusOK}
}
if len(serviceInfo.CodeField) == 0 {
serviceInfo.CodeField = DefaultCodeField
}
if len(serviceInfo.DataField) == 0 {
serviceInfo.DataField = DefaultDataField
}
if len(serviceInfo.MessageField) == 0 {
serviceInfo.MessageField = DefaultMessageField
}
r.lock.Lock()
defer r.lock.Unlock()
if _, exist := r.serviceTable[serviceInfo.Flag]; exist {
return errors.New(serviceInfo.Flag + " already exist")
}
r.serviceTable[serviceInfo.Flag] = serviceInfo
return nil
}
// GetServiceInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:32 2022/6/30
func (r *request) GetServiceInfo(serviceFlag string) (*Service, error) {
var (
serviceInfo *Service
exist bool
)
r.lock.RLock()
defer r.lock.Unlock()
if serviceInfo, exist = r.serviceTable[serviceFlag]; !exist {
return nil, errors.New(serviceFlag + " -> 服务不存在")
}
return serviceInfo, nil
}
// RemoveService 删除一个service
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:46 2022/6/30
func (r *request) RemoveService(serviceFlag string) {
r.lock.Lock()
defer r.lock.Unlock()
delete(r.serviceTable, serviceFlag)
}
// AddServiceApi 向一个service中增加Api
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:26 2022/6/30
func (r *request) AddServiceApi(serviceFlag string, apiConfig *Api) error {
var (
serviceInfo *Service
err error
)
if serviceInfo, err = r.GetServiceInfo(serviceFlag); nil != err {
return err
}
if len(apiConfig.SuccessCodeList) == 0 {
apiConfig.SuccessCodeList = serviceInfo.SuccessCodeList
}
if len(apiConfig.SuccessHttpCodeList) == 0 {
apiConfig.SuccessHttpCodeList = serviceInfo.SuccessHttpCodeList
}
if len(apiConfig.CodeField) == 0 {
apiConfig.CodeField = serviceInfo.CodeField
}
if len(apiConfig.DataField) == 0 {
apiConfig.DataField = serviceInfo.DataField
}
if len(apiConfig.MessageField) == 0 {
apiConfig.MessageField = serviceInfo.MessageField
}
r.lock.Lock()
defer r.lock.Unlock()
if nil == serviceInfo.ApiTable {
serviceInfo.ApiTable = make(map[string]*Api)
}
serviceInfo.ApiTable[apiConfig.Flag] = apiConfig
return nil
}
// GetServiceApi 获取服务api配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:53 2022/6/30
func (r *request) GetServiceApi(serviceFlag string, apiFlag string) (*Service, *Api, error) {
var (
serviceInfo *Service
err error
exist bool
apiInfo *Api
)
if serviceInfo, err = r.GetServiceInfo(serviceFlag); nil != err {
return nil, nil, err
}
r.lock.Lock()
defer r.lock.Unlock()
if apiInfo, exist = serviceInfo.ApiTable[apiFlag]; !exist {
return nil, nil, errors.New(serviceFlag + " : " + apiFlag + " -> api")
}
return serviceInfo, apiInfo, nil
}
// RemoveServiceApi 删除服务下的一个api
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:02 2022/6/30
func (r *request) RemoveServiceApi(serviceFlag string, apiFlag string) {
var (
serviceInfo *Service
err error
)
if serviceInfo, _, err = r.GetServiceApi(serviceFlag, apiFlag); nil != err {
// 不存在无需处理
return
}
r.lock.Lock()
defer r.lock.Unlock()
delete(serviceInfo.ApiTable, apiFlag)
}
// Get ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:25 2022/6/30
func (r *request) Get() error {
return nil
}
// Send 统一的发送请求方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:24 2022/6/30
func (r *request) Send(ctx *gin.Context, serviceFlag string, apiFlag string, parameter map[string]interface{}, receiver interface{}) error {
var (
serviceConfig *Service
apiConfig *Api
err error
fullURL string
client *httpclient.HttpClient
body []byte
response *httpclient.Response
responseBody []byte
code, message, data string
)
if serviceConfig, apiConfig, err = r.GetServiceApi(serviceFlag, apiFlag); nil != err {
return err
}
// 完整的请求地址
fullURL, body = r.getFullURLAndBody(serviceConfig, apiConfig, parameter)
if response, err = client.Do(apiConfig.Method, fullURL, apiConfig.Header, bytes.NewReader(body)); nil != err {
return err
}
if responseBody, err = io.ReadAll(response.Body); nil != err {
return err
}
if !r.httpCodeIsSuccess(response.StatusCode, apiConfig.SuccessHttpCodeList) {
return fmt.Errorf("HTTP状态码异常 : %v -> %v", response.StatusCode, response.Status)
}
// 解析响应的业务数据
code, message, data = r.getCodeAndMessageAndData(apiConfig, responseBody)
if !r.codeIsSuccess(code, apiConfig.SuccessCodeList) {
return fmt.Errorf("业务状态码异常 : %v -> %v", code, message)
}
return parseResponseBody([]byte(data), response.Header.Get("Content-type"))
}
// GetHttpClient 获取client实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:00 2022/6/30
func (r *request) GetHttpClient(header map[string]string, timeout ApiTimeout) *httpclient.HttpClient {
client := httpclient.NewHttpClient()
if timeout.Connect <= 0 {
timeout.Connect = DefaultConnectTimeout
}
if timeout.Read <= 0 {
timeout.Read = DefaultReadTimeout
}
client.WithHeaders(header)
client.WithOption(httpclient.OPT_CONNECTTIMEOUT_MS, time.Duration(timeout.Connect)*time.Millisecond)
client.WithOption(httpclient.OPT_TIMEOUT_MS, time.Duration(timeout.Read)*time.Millisecond)
return client
}
// getFullURL ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:23 2022/6/30
func (r *request) getFullURLAndBody(serviceConfig *Service, apiConfig *Api, parameter map[string]interface{}) (string, []byte) {
fullURL := strings.ReplaceAll(serviceConfig.Domain+"/"+apiConfig.URI, "//", "/")
for name, val := range parameter {
fullURL = strings.ReplaceAll(fullURL, "{{"+name+"}}", fmt.Sprintf("%v", val))
}
parameterPair := make([]string, 0)
var body []byte
switch strings.ToUpper(apiConfig.Method) {
case http.MethodGet:
fallthrough
case http.MethodHead:
fallthrough
case http.MethodOptions:
fallthrough
case http.MethodConnect:
fallthrough
case http.MethodPatch:
fallthrough
case http.MethodTrace:
for name, val := range parameter {
var valStr string
_ = util.ConvertAssign(&valStr, val)
parameterPair = append(parameterPair, fmt.Sprintf("%v=%v", name, valStr))
}
case http.MethodPost:
fallthrough
case http.MethodPut:
fallthrough
case http.MethodDelete:
body, _ = json.Marshal(parameter)
}
query := strings.Join(parameterPair, "&")
if len(query) == 0 {
return strings.ReplaceAll(fullURL, "//", "/"), body
}
return strings.ReplaceAll(fullURL, "//", "/") + "?" + query, body
}
// validateResponseHttpCode 验证http状态码
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:18 2022/6/30
func (r *request) validateResponseHttpCode(apiConfig *Api, response *httpclient.Response) error {
// 判断状态码
isHttpSuccess := false
for _, successCode := range apiConfig.SuccessHttpCodeList {
if successCode == response.StatusCode {
isHttpSuccess = true
break
}
}
if !isHttpSuccess {
return fmt.Errorf("http响应状态码异常 -> %v", response.StatusCode)
}
return nil
}
// getCodeAndMessageAndData 读取业务状态码 + 文案 + 数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:20 2022/6/30
func (r *request) getCodeAndMessageAndData(apiConfig *Api, responseBody []byte) (string, string, string) {
var (
code = SpecialFiledVal
message = SpecialFiledVal
data string
)
if apiConfig.CodeField != SpecialFiledVal {
code = gjson.Get(string(responseBody), apiConfig.CodeField).String()
}
if apiConfig.MessageField != SpecialFiledVal {
message = gjson.Get(string(responseBody), apiConfig.MessageField).String()
}
if apiConfig.DataField == SpecialFiledVal {
data = string(responseBody)
} else {
data = gjson.Get(string(responseBody), apiConfig.DataField).String()
}
return code, message, data
}
// codeIsSuccess 判断业务状态码是否为成功
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:27 2022/6/30
func (r *request) codeIsSuccess(input string, successCodeList []string) bool {
for _, item := range successCodeList {
if item == input {
return true
}
}
return false
}
// httpCodeIsSuccess http状态码是否为成功
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:31 2022/6/30
func (r *request) httpCodeIsSuccess(input int, successCodeList []int) bool {
for _, item := range successCodeList {
if item == input {
return true
}
}
return false
}