From 2765ef0f5aee05b9534be237aa83b1b13f34acb9 Mon Sep 17 00:00:00 2001 From: Patrick <dreamlike.sky@foxmail.com> Date: Sun, 19 Jan 2020 23:01:31 +0800 Subject: [PATCH] rest configs and invoker --- common/extension/rest_client.go | 20 +++ common/extension/rest_config_reader.go | 24 ++++ config/consumer_config.go | 1 + config/provider_config.go | 1 + go.mod | 1 + go.sum | 4 + protocol/rest/rest_client/resty_client.go | 69 ++++++++++ protocol/rest/rest_config_initializer.go | 130 ++++++++++++++++++ .../default_config_reader.go | 89 ++++++++++++ protocol/rest/rest_interface/rest_client.go | 25 ++++ protocol/rest/rest_interface/rest_config.go | 28 ++++ .../rest/rest_interface/rest_config_reader.go | 6 + .../rest/rest_interface/rest_method_config.go | 17 +++ protocol/rest/rest_invoker.go | 65 ++++++--- protocol/rest/rest_invoker_test.go | 70 ++++++++++ protocol/rest/rest_protocol.go | 27 +++- protocol/rest/rest_protocol_test.go | 42 ++++++ protocol/rest/{ => rest_server}/gin_server.go | 2 +- 18 files changed, 600 insertions(+), 21 deletions(-) create mode 100644 common/extension/rest_client.go create mode 100644 common/extension/rest_config_reader.go create mode 100644 protocol/rest/rest_client/resty_client.go create mode 100644 protocol/rest/rest_config_initializer.go create mode 100644 protocol/rest/rest_config_reader/default_config_reader.go create mode 100644 protocol/rest/rest_interface/rest_client.go create mode 100644 protocol/rest/rest_interface/rest_config.go create mode 100644 protocol/rest/rest_interface/rest_config_reader.go create mode 100644 protocol/rest/rest_interface/rest_method_config.go create mode 100644 protocol/rest/rest_invoker_test.go create mode 100644 protocol/rest/rest_protocol_test.go rename protocol/rest/{ => rest_server}/gin_server.go (98%) diff --git a/common/extension/rest_client.go b/common/extension/rest_client.go new file mode 100644 index 000000000..35e6fb538 --- /dev/null +++ b/common/extension/rest_client.go @@ -0,0 +1,20 @@ +package extension + +import ( + "github.com/apache/dubbo-go/protocol/rest/rest_interface" +) + +var ( + restClients = make(map[string]func(restOptions *rest_interface.RestOptions) rest_interface.RestClient) +) + +func SetRestClient(name string, fun func(restOptions *rest_interface.RestOptions) rest_interface.RestClient) { + restClients[name] = fun +} + +func GetRestClient(name string, restOptions *rest_interface.RestOptions) rest_interface.RestClient { + if restClients[name] == nil { + panic("restClient for " + name + " is not existing, make sure you have import the package.") + } + return restClients[name](restOptions) +} diff --git a/common/extension/rest_config_reader.go b/common/extension/rest_config_reader.go new file mode 100644 index 000000000..be76689b9 --- /dev/null +++ b/common/extension/rest_config_reader.go @@ -0,0 +1,24 @@ +package extension + +import ( + "github.com/apache/dubbo-go/protocol/rest/rest_interface" +) + +var ( + restConfigReaders = make(map[string]func() rest_interface.RestConfigReader) +) + +func SetRestConfigReader(name string, fun func() rest_interface.RestConfigReader) { + restConfigReaders[name] = fun +} + +func GetRestConfigReader(name string) rest_interface.RestConfigReader { + if name == "" { + name = "default" + } + if restConfigReaders[name] == nil { + panic("restConfigReaders for " + name + " is not existing, make sure you have import the package.") + } + return restConfigReaders[name]() + +} diff --git a/config/consumer_config.go b/config/consumer_config.go index 72f60b5f7..38b05c617 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -58,6 +58,7 @@ type ConsumerConfig struct { ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"` FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` + RestConfigType string `default:"default" yaml:"rest_config_type" json:"rest_config_type,omitempty" property:"rest_config_type"` } func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { diff --git a/config/provider_config.go b/config/provider_config.go index 0fed44c81..4958bd7ab 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -50,6 +50,7 @@ type ProviderConfig struct { ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" ` FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` + RestConfigType string `default:"default" yaml:"rest_config_type" json:"rest_config_type,omitempty" property:"rest_config_type"` } func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { diff --git a/go.mod b/go.mod index 80987daec..efe505bcd 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/gin-gonic/gin v1.5.0 github.com/go-errors/errors v1.0.1 // indirect + github.com/go-resty/resty/v2 v2.1.0 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/mock v1.3.1 github.com/google/btree v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6ff826ba5..f16edfcf9 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,8 @@ github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotf github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE= +github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4 h1:1LlmVz15APoKz9dnm5j2ePptburJlwEH+/v/pUuoxck= github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -507,6 +509,8 @@ golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= diff --git a/protocol/rest/rest_client/resty_client.go b/protocol/rest/rest_client/resty_client.go new file mode 100644 index 000000000..de6f075bb --- /dev/null +++ b/protocol/rest/rest_client/resty_client.go @@ -0,0 +1,69 @@ +package rest_client + +import ( + "context" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + "github.com/go-resty/resty/v2" + "net" + "net/http" + "path" + "time" +) + +const ( + RESTY = "resty" +) + +func init() { + extension.SetRestClient(RESTY, GetRestyClient) +} + +var restyClient *RestyClient + +type RestyClient struct { + client *resty.Client +} + +func NewRestyClient(restOption *rest_interface.RestOptions) *RestyClient { + if restOption.ConnectTimeout == 0 { + restOption.ConnectTimeout = 3 + } + if restOption.RequestTimeout == 0 { + restOption.RequestTimeout = 3 + } + client := resty.New() + client.SetTransport( + &http.Transport{ + DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + c, err := net.DialTimeout(network, addr, restOption.ConnectTimeout*time.Second) + if err != nil { + return nil, err + } + err = c.SetDeadline(time.Now().Add(restOption.RequestTimeout * time.Second)) + if err != nil { + return nil, err + } + return c, nil + }, + }) + return &RestyClient{ + client: client, + } +} + +func (rc *RestyClient) Do(restRequest *rest_interface.RestRequest, res interface{}) error { + _, err := rc.client.R(). + SetHeader("Content-Type", restRequest.Consumes). + SetHeader("Accept", restRequest.Produces). + SetPathParams(restRequest.PathParams). + SetQueryParams(restRequest.QueryParams). + SetBody(restRequest.Body). + SetResult(res). + Execute(restRequest.Method, "http://"+path.Join(restRequest.Location, restRequest.Path)) + return err +} + +func GetRestyClient(restOptions *rest_interface.RestOptions) rest_interface.RestClient { + return NewRestyClient(restOptions) +} diff --git a/protocol/rest/rest_config_initializer.go b/protocol/rest/rest_config_initializer.go new file mode 100644 index 000000000..8dc8d340b --- /dev/null +++ b/protocol/rest/rest_config_initializer.go @@ -0,0 +1,130 @@ +package rest + +import ( + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/protocol/rest/rest_config_reader" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + "strconv" + "strings" +) + +var ( + restConsumerConfig *rest_interface.RestConsumerConfig + restProviderConfig *rest_interface.RestProviderConfig + restConsumerServiceConfigMap map[string]*rest_interface.RestConfig + restProviderServiceConfigMap map[string]*rest_interface.RestConfig +) + +func init() { + initConsumerRestConfig() + initProviderRestConfig() +} + +func initConsumerRestConfig() { + consumerConfigType := config.GetConsumerConfig().RestConfigType + consumerConfigReader := extension.GetRestConfigReader(consumerConfigType) + restConsumerConfig = consumerConfigReader.ReadConsumerConfig() + if restConsumerConfig == nil { + return + } + for _, rc := range restConsumerConfig.RestConfigMap { + rc.Client = getNotEmptyStr(rc.Client, restConsumerConfig.Client, "resty") + rc.RestMethodConfigsMap = initMethodConfigMap(rc, restConsumerConfig.Consumes, restConsumerConfig.Produces) + restConsumerServiceConfigMap[rc.InterfaceName] = rc + } +} + +func initProviderRestConfig() { + providerConfigType := config.GetProviderConfig().RestConfigType + providerConfigReader := extension.GetRestConfigReader(providerConfigType) + restProviderConfig = providerConfigReader.ReadProviderConfig() + if restProviderConfig == nil { + return + } + for _, rc := range restProviderConfig.RestConfigMap { + rc.Server = getNotEmptyStr(rc.Server, restProviderConfig.Server) + rc.RestMethodConfigsMap = initMethodConfigMap(rc, restProviderConfig.Consumes, restProviderConfig.Produces) + restProviderServiceConfigMap[rc.InterfaceName] = rc + } +} + +func initMethodConfigMap(rc *rest_interface.RestConfig, consumes string, produces string) map[string]*rest_interface.RestMethodConfig { + mcm := make(map[string]*rest_interface.RestMethodConfig, len(rc.RestMethodConfigs)) + for _, mc := range rc.RestMethodConfigs { + mc.InterfaceName = rc.InterfaceName + mc.Path = rc.Path + mc.Path + mc.Consumes = getNotEmptyStr(mc.Consumes, rc.Consumes, consumes) + mc.Produces = getNotEmptyStr(mc.Produces, rc.Produces, produces) + mc.MethodType = getNotEmptyStr(mc.MethodType, rc.MethodType) + mc = transformMethodConfig(mc) + mcm[mc.MethodName] = mc + } + return mcm +} + +func getNotEmptyStr(args ...string) string { + var r string + for _, t := range args { + if len(r) == 0 { + r = t + } else { + break + } + } + return r +} + +func transformMethodConfig(methodConfig *rest_interface.RestMethodConfig) *rest_interface.RestMethodConfig { + if len(methodConfig.PathParamsMap) == 0 && len(methodConfig.PathParams) > 0 { + paramsMap, err := parseParamsString2Map(methodConfig.PathParams) + if err != nil { + logger.Warnf("[Rest Config] Path Param parse error:%v", err) + } else { + methodConfig.PathParamsMap = paramsMap + } + } + if len(methodConfig.QueryParamsMap) == 0 && len(methodConfig.QueryParams) > 0 { + paramsMap, err := parseParamsString2Map(methodConfig.PathParams) + if err != nil { + logger.Warnf("[Rest Config] Argument Param parse error:%v", err) + } else { + methodConfig.QueryParamsMap = paramsMap + } + } + if len(methodConfig.BodyMap) == 0 && len(methodConfig.Body) > 0 { + paramsMap, err := parseParamsString2Map(methodConfig.Body) + if err != nil { + logger.Warnf("[Rest Config] Body Param parse error:%v", err) + } else { + methodConfig.BodyMap = paramsMap + } + } + return methodConfig +} + +func parseParamsString2Map(params string) (map[int]string, error) { + m := make(map[int]string) + for _, p := range strings.Split(params, ",") { + pa := strings.Split(p, ":") + key, err := strconv.Atoi(pa[0]) + if err != nil { + return nil, err + } + m[key] = pa[1] + } + return m, nil +} + +func GetRestConsumerServiceConfig(service string) *rest_interface.RestConfig { + return restConsumerServiceConfigMap[service] +} + +func GetRestProviderServiceConfig(service string) *rest_interface.RestConfig { + return restProviderServiceConfigMap[service] +} + +func SetRestConsumerServiceConfigMap(configMap map[string]*rest_interface.RestConfig) { + restConsumerServiceConfigMap = configMap +} diff --git a/protocol/rest/rest_config_reader/default_config_reader.go b/protocol/rest/rest_config_reader/default_config_reader.go new file mode 100644 index 000000000..04b29b368 --- /dev/null +++ b/protocol/rest/rest_config_reader/default_config_reader.go @@ -0,0 +1,89 @@ +package rest_config_reader + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + perrors "github.com/pkg/errors" + "gopkg.in/yaml.v2" + "io/ioutil" + "os" + "path" +) + +const ( + DEFAULT_READER = "default" +) + +var ( + defaultConfigReader *DefaultConfigReader +) + +func init() { + extension.SetRestConfigReader(DEFAULT_READER, GetDefaultConfigReader) +} + +type DefaultConfigReader struct { +} + +func NewDefaultConfigReader() *DefaultConfigReader { + return &DefaultConfigReader{} +} + +func (dcr *DefaultConfigReader) ReadConsumerConfig() *rest_interface.RestConsumerConfig { + confConFile := os.Getenv(constant.CONF_CONSUMER_FILE_PATH) + if confConFile == "" { + logger.Warnf("rest consumer configure(consumer) file name is nil") + return nil + } + if path.Ext(confConFile) != ".yml" { + logger.Warnf("rest consumer configure file name{%v} suffix must be .yml", confConFile) + return nil + } + confFileStream, err := ioutil.ReadFile(confConFile) + if err != nil { + logger.Warnf("ioutil.ReadFile(file:%s) = error:%v", confConFile, perrors.WithStack(err)) + return nil + } + restConsumerConfig := &rest_interface.RestConsumerConfig{} + err = yaml.Unmarshal(confFileStream, restConsumerConfig) + if err != nil { + logger.Warnf("yaml.Unmarshal() = error:%v", perrors.WithStack(err)) + return nil + } + return restConsumerConfig +} + +func (dcr *DefaultConfigReader) ReadProviderConfig() *rest_interface.RestProviderConfig { + confProFile := os.Getenv(constant.CONF_PROVIDER_FILE_PATH) + if len(confProFile) == 0 { + logger.Warnf("rest provider configure(provider) file name is nil") + return nil + } + + if path.Ext(confProFile) != ".yml" { + logger.Warnf("rest provider configure file name{%v} suffix must be .yml", confProFile) + return nil + } + confFileStream, err := ioutil.ReadFile(confProFile) + if err != nil { + logger.Warnf("ioutil.ReadFile(file:%s) = error:%v", confProFile, perrors.WithStack(err)) + return nil + } + restProviderConfig := &rest_interface.RestProviderConfig{} + err = yaml.Unmarshal(confFileStream, restProviderConfig) + if err != nil { + logger.Warnf("yaml.Unmarshal() = error:%v", perrors.WithStack(err)) + return nil + } + + return restProviderConfig +} + +func GetDefaultConfigReader() rest_interface.RestConfigReader { + if defaultConfigReader == nil { + defaultConfigReader = NewDefaultConfigReader() + } + return defaultConfigReader +} diff --git a/protocol/rest/rest_interface/rest_client.go b/protocol/rest/rest_interface/rest_client.go new file mode 100644 index 000000000..6d0515daa --- /dev/null +++ b/protocol/rest/rest_interface/rest_client.go @@ -0,0 +1,25 @@ +package rest_interface + +import ( + "time" +) + +type RestOptions struct { + RequestTimeout time.Duration + ConnectTimeout time.Duration +} + +type RestRequest struct { + Location string + Path string + Produces string + Consumes string + Method string + PathParams map[string]string + QueryParams map[string]string + Body map[string]interface{} +} + +type RestClient interface { + Do(request *RestRequest, res interface{}) error +} diff --git a/protocol/rest/rest_interface/rest_config.go b/protocol/rest/rest_interface/rest_config.go new file mode 100644 index 000000000..1c368d509 --- /dev/null +++ b/protocol/rest/rest_interface/rest_config.go @@ -0,0 +1,28 @@ +package rest_interface + +type RestConfig struct { + InterfaceName string `required:"true" yaml:"interface" json:"interface,omitempty" property:"interface"` + Url string `yaml:"url" json:"url,omitempty" property:"url"` + Path string `yaml:"rest_path" json:"rest_path,omitempty" property:"rest_path"` + Produces string `yaml:"rest_produces" json:"rest_produces,omitempty" property:"rest_produces"` + Consumes string `yaml:"rest_consumes" json:"rest_consumes,omitempty" property:"rest_consumes"` + MethodType string `yaml:"rest_method" json:"rest_method,omitempty" property:"rest_method"` + Client string `yaml:"rest_client" json:"rest_client,omitempty" property:"rest_client"` + Server string `yaml:"rest_server" json:"rest_server,omitempty" property:"rest_server"` + RestMethodConfigs []*RestMethodConfig `yaml:"methods" json:"methods,omitempty" property:"methods"` + RestMethodConfigsMap map[string]*RestMethodConfig +} + +type RestConsumerConfig struct { + Client string `default:"resty" yaml:"rest_client" json:"rest_client,omitempty" property:"rest_client"` + Produces string `yaml:"rest_produces" json:"rest_produces,omitempty" property:"rest_produces"` + Consumes string `yaml:"rest_consumes" json:"rest_consumes,omitempty" property:"rest_consumes"` + RestConfigMap map[string]*RestConfig `yaml:"references" json:"references,omitempty" property:"references"` +} + +type RestProviderConfig struct { + Server string `default:"go-restful" yaml:"rest_server" json:"rest_server,omitempty" property:"rest_server"` + Produces string `yaml:"rest_produces" json:"rest_produces,omitempty" property:"rest_produces"` + Consumes string `yaml:"rest_consumes" json:"rest_consumes,omitempty" property:"rest_consumes"` + RestConfigMap map[string]*RestConfig `yaml:"services" json:"services,omitempty" property:"services"` +} diff --git a/protocol/rest/rest_interface/rest_config_reader.go b/protocol/rest/rest_interface/rest_config_reader.go new file mode 100644 index 000000000..0dea65719 --- /dev/null +++ b/protocol/rest/rest_interface/rest_config_reader.go @@ -0,0 +1,6 @@ +package rest_interface + +type RestConfigReader interface { + ReadConsumerConfig() *RestConsumerConfig + ReadProviderConfig() *RestProviderConfig +} diff --git a/protocol/rest/rest_interface/rest_method_config.go b/protocol/rest/rest_interface/rest_method_config.go new file mode 100644 index 000000000..c9430644a --- /dev/null +++ b/protocol/rest/rest_interface/rest_method_config.go @@ -0,0 +1,17 @@ +package rest_interface + +type RestMethodConfig struct { + InterfaceName string + MethodName string `required:"true" yaml:"name" json:"name,omitempty" property:"name"` + Url string `yaml:"url" json:"url,omitempty" property:"url"` + Path string `yaml:"rest_path" json:"rest_path,omitempty" property:"rest_path"` + Produces string `yaml:"rest_produces" json:"rest_produces,omitempty" property:"rest_produces"` + Consumes string `yaml:"rest_consumes" json:"rest_consumes,omitempty" property:"rest_consumes"` + MethodType string `yaml:"rest_method" json:"rest_method,omitempty" property:"rest_method"` + PathParams string `yaml:"rest_path_params" json:"rest_path_params,omitempty" property:"rest_path_params"` + PathParamsMap map[int]string + QueryParams string `yaml:"rest_query_params" json:"rest_query_params,omitempty" property:"rest_query_params"` + QueryParamsMap map[int]string + Body string `yaml:"rest_body" json:"rest_body,omitempty" property:"rest_body"` + BodyMap map[int]string +} diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go index 0580b8389..ca2260632 100644 --- a/protocol/rest/rest_invoker.go +++ b/protocol/rest/rest_invoker.go @@ -1,31 +1,62 @@ package rest import ( + "fmt" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" - "net/http" + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" ) type RestInvoker struct { protocol.BaseInvoker - client *http.Client + client rest_interface.RestClient + restMethodConfigMap map[string]*rest_interface.RestMethodConfig } -func NewRestInvoker() *RestInvoker { - return &RestInvoker{} +func NewRestInvoker(url common.URL, client rest_interface.RestClient, restMethodConfig map[string]*rest_interface.RestMethodConfig) *RestInvoker { + return &RestInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + client: client, + restMethodConfigMap: restMethodConfig, + } } func (ri *RestInvoker) Invoke(invocation protocol.Invocation) protocol.Result { - // TODO 棣栧厛锛屽皢鏈湴璋冪敤鍜孯eferRestConfig锛岀粨鍚堝湪涓€璧凤紝鍒拌繖涓€姝ュ畬鎴愪簡Service -> Rest鐨勭粦瀹氾紱 - // 绗竴姝ュ畬鎴愮殑浜х墿鏄竴浠絤etadata鍜岃姹傚弬鏁帮紝鏈楠ゅ皢鍒╃敤metadata鍜岃姹傚弬鏁版潵鏋勯€爃ttp璇锋眰锛屽寘鎷弬鏁板簭鍒楀寲锛宧eader璁惧畾锛涘叾涓弬鏁板簭鍒楀寲鍙互鏄疛son锛屼篃鍙互鏄疿ML锛屽悗缁彲浠ユ墿灞曞埌澶氬獟浣撶瓑锛� - // http client鍙戦€佽姹傘€傝姝ラ瑕佸厑璁哥敤鎴疯嚜瀹氫箟鍏惰繛鎺ュ弬鏁帮紝渚嬪瓒呮椂鏃堕棿绛夛紱 - //var ( - // result protocol.RPCResult - //) - //req, err := http.NewRequest("method", "url", bytes.NewBuffer([]byte{})) - //resp, err := ri.client.Do(req) - //defer resp.Body.Close() - //body, err := ioutil.ReadAll(resp.Body) - //result.Rest = invocation.Reply() - //return &result - return nil + inv := invocation.(*invocation_impl.RPCInvocation) + methodConfig := ri.restMethodConfigMap[inv.MethodName()] + var result protocol.RPCResult + if methodConfig == nil { + logger.Errorf("[RestInvoker]Rest methodConfig:%s is nill", inv.MethodName()) + return nil + } + pathParams := make(map[string]string) + queryParams := make(map[string]string) + bodyParams := make(map[string]interface{}) + for key, value := range methodConfig.PathParamsMap { + pathParams[value] = fmt.Sprintf("%v", inv.Arguments()[key]) + } + for key, value := range methodConfig.QueryParamsMap { + queryParams[value] = fmt.Sprintf("%v", inv.Arguments()[key]) + } + for key, value := range methodConfig.BodyMap { + bodyParams[value] = inv.Arguments()[key] + } + req := &rest_interface.RestRequest{ + Location: ri.GetUrl().Location, + Produces: methodConfig.Produces, + Consumes: methodConfig.Consumes, + Method: methodConfig.MethodType, + Path: methodConfig.Path, + PathParams: pathParams, + QueryParams: queryParams, + Body: bodyParams, + } + result.Err = ri.client.Do(req, inv.Reply()) + if result.Err == nil { + result.Rest = inv.Reply() + } + return &result + } diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go new file mode 100644 index 000000000..ee8ba7b6f --- /dev/null +++ b/protocol/rest/rest_invoker_test.go @@ -0,0 +1,70 @@ +package rest + +import ( + "context" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/rest/rest_client" + _ "github.com/apache/dubbo-go/protocol/rest/rest_config_reader" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +type User struct { +} + +func TestRestInvoker_Invoke(t *testing.T) { + // Refer + proto := GetRestProtocol() + url, err := common.NewURL(context.Background(), "rest://127.0.0.1:8888/com.ikurento.user.UserProvider?anyhost=true&"+ + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + "side=provider&timeout=3000×tamp=1556509797245") + assert.NoError(t, err) + con := config.ConsumerConfig{ + ConnectTimeout: 1 * time.Second, + RequestTimeout: 1 * time.Second, + RestConfigType: "default", + } + config.SetConsumerConfig(con) + configMap := make(map[string]*rest_interface.RestConfig) + methodConfigMap := make(map[string]*rest_interface.RestMethodConfig) + methodConfigMap["GetUser"] = &rest_interface.RestMethodConfig{ + InterfaceName: "", + MethodName: "GetUser", + Path: "/GetUser", + Produces: "application/json", + Consumes: "application/json", + MethodType: "GET", + PathParams: "", + PathParamsMap: nil, + QueryParams: "", + QueryParamsMap: nil, + Body: "", + BodyMap: nil, + } + configMap["com.ikurento.user.UserProvider"] = &rest_interface.RestConfig{ + RestMethodConfigsMap: methodConfigMap, + } + restClient := rest_client.GetRestyClient(&rest_interface.RestOptions{ConnectTimeout: 5 * time.Second, RequestTimeout: 5 * time.Second}) + invoker := NewRestInvoker(url, restClient, methodConfigMap) + user := &User{} + inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), + invocation.WithArguments([]interface{}{"1", "username"}), invocation.WithReply(user)) + invoker.Invoke(inv) + + // make sure url + eq := invoker.GetUrl().URLEqual(url) + assert.True(t, eq) + + // make sure invokers after 'Destroy' + invokersLen := len(proto.(*RestProtocol).Invokers()) + assert.Equal(t, 1, invokersLen) + proto.Destroy() + invokersLen = len(proto.(*RestProtocol).Invokers()) + assert.Equal(t, 0, invokersLen) +} diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go index 2f4e945aa..8801f1efe 100644 --- a/protocol/rest/rest_protocol.go +++ b/protocol/rest/rest_protocol.go @@ -2,13 +2,24 @@ package rest import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + "time" ) var ( restProtocol *RestProtocol ) +const REST = "rest" + +func init() { + extension.SetProtocol(REST, GetRestProtocol) +} + type RestProtocol struct { protocol.BaseProtocol } @@ -22,18 +33,28 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter { // Server鍦‥xport鐨勬椂鍊欏苟涓嶅仛浠€涔堜簨鎯呫€備絾鏄湪鎺ュ彈鍒拌姹傜殑鏃跺€欙紝瀹冮渶瑕佽礋璐f墽琛屽弽搴忓垪鍖栫殑杩囩▼; // http server鏄竴涓娊璞¢殧绂诲眰銆傚畠鍐呴儴鍏佽浣跨敤beego鎴栬€単in鏉ヤ綔涓簑eb鏈嶅姟鍣紝鎺ユ敹璇锋眰锛岀敤鎴峰彲浠ユ墿灞曡嚜宸辩殑瀹炵幇锛� - // create gin_server - // save gin_server in map return nil } func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker { // create rest_invoker - return nil + var requestTimeout = config.GetConsumerConfig().RequestTimeout + + requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) + connectTimeout := config.GetConsumerConfig().ConnectTimeout + if t, err := time.ParseDuration(requestTimeoutStr); err == nil { + requestTimeout = t + } + restConfig := GetRestConsumerServiceConfig(url.Service()) + restClient := extension.GetRestClient(restConfig.Client, &rest_interface.RestOptions{RequestTimeout: requestTimeout, ConnectTimeout: connectTimeout}) + invoker := NewRestInvoker(url, restClient, restConfig.RestMethodConfigsMap) + rp.SetInvokers(invoker) + return invoker } func (rp *RestProtocol) Destroy() { // destroy rest_server + rp.BaseProtocol.Destroy() } func GetRestProtocol() protocol.Protocol { diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go new file mode 100644 index 000000000..e77fde8ad --- /dev/null +++ b/protocol/rest/rest_protocol_test.go @@ -0,0 +1,42 @@ +package rest + +import ( + "context" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol/rest/rest_interface" + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestRestProtocol_Refer(t *testing.T) { + // Refer + proto := GetRestProtocol() + url, err := common.NewURL(context.Background(), "rest://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + "side=provider&timeout=3000×tamp=1556509797245") + assert.NoError(t, err) + con := config.ConsumerConfig{ + ConnectTimeout: 5 * time.Second, + RequestTimeout: 5 * time.Second, + } + config.SetConsumerConfig(con) + configMap := make(map[string]*rest_interface.RestConfig) + configMap["com.ikurento.user.UserProvider"] = &rest_interface.RestConfig{} + SetRestConsumerServiceConfigMap(configMap) + invoker := proto.Refer(url) + + // make sure url + eq := invoker.GetUrl().URLEqual(url) + assert.True(t, eq) + + // make sure invokers after 'Destroy' + invokersLen := len(proto.(*RestProtocol).Invokers()) + assert.Equal(t, 1, invokersLen) + proto.Destroy() + invokersLen = len(proto.(*RestProtocol).Invokers()) + assert.Equal(t, 0, invokersLen) +} diff --git a/protocol/rest/gin_server.go b/protocol/rest/rest_server/gin_server.go similarity index 98% rename from protocol/rest/gin_server.go rename to protocol/rest/rest_server/gin_server.go index b05792617..0525e2f83 100644 --- a/protocol/rest/gin_server.go +++ b/protocol/rest/rest_server/gin_server.go @@ -1,4 +1,4 @@ -package rest +package rest_server import ( "context" -- GitLab