// 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 } // 解析响应的业务数据 code, message, data = r.getCodeAndMessageAndData(apiConfig, responseBody) if !r.codeIsSuccess(code, apiConfig.SuccessCodeList) { return fmt.Errorf("业务状态码异常 : %v -> %v", code, message) } return nil } // 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 }