Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Please refer to [HOWTO.md](HOWTO.md) for detailed instructions on running the sa
* `rpc/jsonrpc`: JSON-RPC protocol example.
* `rpc/triple`: Triple protocol example with multiple serialization formats.
* `rpc/triple/openapi`: Demonstrates how to enable OpenAPI documentation for Triple protocol services, including versioned services and non-IDL services.
* `rpc/rest`: Rest protocol example.
* `streaming`: Streaming RPC example, also includes Go–Java interoperability when both use streaming.
* `task`: Task scheduling and execution example.
* `timeout`: Demonstrates timeout handling in Dubbo-go.
Expand Down
1 change: 1 addition & 0 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
* `rpc/jsonrpc`:基于 JSON-RPC 协议的示例。
* `rpc/triple`:Triple 协议示例,涵盖多种序列化方式。
* `rpc/triple/openapi`:演示如何为 Triple 协议服务启用 OpenAPI 文档,包括多版本服务和非 IDL 服务的注册。
* `rpc/rest`:基于 Rest 协议的示例。
* `streaming`:流式 RPC 调用示例,并包含了Dubbo-go与Dubbo-java同时使用流式传输的互操作示例。
* `task`:任务调度与执行示例。
* `timeout`:Dubbo-go 超时处理示例。
Expand Down
76 changes: 76 additions & 0 deletions rpc/rest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Dubbo-Go REST sample

This sample validates the Dubbo-Go REST protocol with direct URL, interface-level registry, and application-level service discovery. It uses an explicit REST mapping for path, query, header, and body arguments.

## Run

Start the provider with the default Nacos application-level service discovery mode:

```bash
go run ./rpc/rest/go-server/cmd
```

Run the Dubbo-Go REST consumer in another terminal:

```bash
go run ./rpc/rest/go-client/cmd
```

Expected client output:

```text
REST response: userID=101 name=dubbo-go traceID=trace-rest-basic message=body-from-dubbo-rest-client greeting="hello dubbo-go, userID=101, traceID=trace-rest-basic, message=body-from-dubbo-rest-client"
```

The same provider is also a normal HTTP endpoint:

```bash
curl -s \
-X POST 'http://127.0.0.1:20080/api/v1/users/202/greeting?name=curl' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Trace-ID: trace-curl' \
-d '{"message":"body-from-curl"}'
```

## What this proves

The provider URL only supplies the network target, for example `rest://127.0.0.1:20080/org.apache.dubbo.samples.rest.GreetingService`.

The actual REST call shape comes from the REST method config in `api/rest_config.go`:

- method: `POST`
- path: `/api/v1/users/{userID}/greeting`
- path parameter: argument `0` -> `userID`
- query parameter: argument `1` -> `name`
- header: argument `2` -> `X-Trace-ID`
- body: argument `3`

So this sample intentionally separates "provider address" from "REST HTTP mapping".

## Registry service discovery

The provider and consumer support these flags:

- `-registry=direct|zookeeper|nacos`
- `-registry-type=interface|service|all`

The default is `-registry=nacos -registry-type=service`.

`interface` means the registry stores the callable `rest://...` provider URL directly. `service` means the registry stores the application instance; the consumer finds the application by service mapping, fetches metadata from the provider metadata service, then reconstructs the `rest://...` provider URL from the discovered instance and service metadata. `all` registers both forms.

Run direct URL mode:

```bash
go run ./rpc/rest/go-server/cmd -registry=direct
go run ./rpc/rest/go-client/cmd -registry=direct
```

Run ZooKeeper interface-level registration:

```bash
go run ./rpc/rest/go-server/cmd -registry=zookeeper -registry-type=interface
go run ./rpc/rest/go-client/cmd -registry=zookeeper -registry-type=interface
```

All modes should print the same `REST response: ...` line from the consumer.
92 changes: 92 additions & 0 deletions rpc/rest/README_CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Dubbo-Go REST 示例

这个示例用于验证 Dubbo-Go REST 协议的三种地址获取方式:

- 直连 URL
- 接口级注册
- 应用级服务发现

示例通过本地 REST 配置显式声明 HTTP 方法、路径参数、查询参数、请求头和请求体映射。

## 运行

默认模式是 Nacos 应用级服务发现:

```bash
go run ./rpc/rest/go-server/cmd
```

在另一个终端运行消费者:

```bash
go run ./rpc/rest/go-client/cmd
```

期望输出:

```text
REST response: userID=101 name=dubbo-go traceID=trace-rest-basic message=body-from-dubbo-rest-client greeting="hello dubbo-go, userID=101, traceID=trace-rest-basic, message=body-from-dubbo-rest-client"
```

Provider 同时也是一个普通 HTTP 服务,可以直接用 `curl` 调用:

```bash
curl -s \
-X POST 'http://127.0.0.1:20080/api/v1/users/202/greeting?name=curl' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Trace-ID: trace-curl' \
-d '{"message":"body-from-curl"}'
```

## REST 映射

Provider URL 只提供网络目标,例如:

```text
rest://127.0.0.1:20080/org.apache.dubbo.samples.rest.GreetingService
```

真正的 REST 调用形态来自 `api/rest_config.go`:

- HTTP 方法:`POST`
- 路径:`/api/v1/users/{userID}/greeting`
- 路径参数:参数 `0` -> `userID`
- 查询参数:参数 `1` -> `name`
- 请求头:参数 `2` -> `X-Trace-ID`
- 请求体:参数 `3`

也就是说,这个示例刻意区分了“服务地址发现”和“REST HTTP 映射”。

## 服务发现模式

Provider 和 Consumer 都支持以下参数:

- `-registry=direct|zookeeper|nacos`
- `-registry-type=interface|service|all`

默认值是:

```bash
-registry=nacos -registry-type=service
```

`interface` 表示注册中心直接保存可调用的 `rest://...` provider URL。
`service` 表示注册中心保存应用实例;consumer 先通过 service mapping 找到应用,再通过 metadata service 获取服务元数据,最后根据应用实例和 `ServiceInfo` 重建 `rest://...` provider URL。
`all` 表示同时注册接口级和应用级两种数据。

## 直连模式

```bash
go run ./rpc/rest/go-server/cmd -registry=direct
go run ./rpc/rest/go-client/cmd -registry=direct
```

## ZooKeeper 接口级注册

```bash
go run ./rpc/rest/go-server/cmd -registry=zookeeper -registry-type=interface
go run ./rpc/rest/go-client/cmd -registry=zookeeper -registry-type=interface
```

所有模式下,consumer 都应该输出相同的 `REST response: ...`。
41 changes: 41 additions & 0 deletions rpc/rest/api/greeting.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package api

const (
InterfaceName = "org.apache.dubbo.samples.rest.GreetingService"
MethodGetGreeting = "GetGreeting"
)

type GreetingService struct{}

func (*GreetingService) Reference() string {
return InterfaceName
}

type GreetingRequest struct {
Message string `json:"message"`
}

type GreetingResponse struct {
UserID int `json:"userID"`
Name string `json:"name"`
TraceID string `json:"traceID"`
Message string `json:"message"`
Greeting string `json:"greeting"`
}
71 changes: 71 additions & 0 deletions rpc/rest/api/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package api

import (
"fmt"
)

import (
"dubbo.apache.org/dubbo-go/v3/registry"
)

const (
ServerAppName = "dubbo_rest_basic_server"
ClientAppName = "dubbo_rest_basic_client"

RegistryDirect = "direct"
RegistryZookeeper = "zookeeper"
RegistryNacos = "nacos"

RegistryTypeInterface = "interface"
RegistryTypeService = "service"
RegistryTypeAll = "all"

DefaultRegistry = RegistryNacos
DefaultRegistryType = RegistryTypeService
)

func RegistryOptions(name string, registryType string) ([]registry.Option, error) {
if name == RegistryDirect || name == "" {
return nil, nil
}

var opts []registry.Option
switch name {
case RegistryZookeeper:
opts = append(opts, registry.WithZookeeper(), registry.WithAddress("127.0.0.1:2181"))
case RegistryNacos:
opts = append(opts, registry.WithNacos(), registry.WithAddress("127.0.0.1:8848"))
default:
return nil, fmt.Errorf("unsupported registry %q, use direct, zookeeper, or nacos", name)
}

opts = append(opts, registry.WithoutUseAsConfigCenter())
switch registryType {
case "", RegistryTypeInterface:
opts = append(opts, registry.WithRegisterInterface())
case RegistryTypeService:
opts = append(opts, registry.WithRegisterService())
case RegistryTypeAll:
opts = append(opts, registry.WithRegisterServiceAndInterface())
default:
return nil, fmt.Errorf("unsupported registry type %q, use interface, service, or all", registryType)
}
return opts, nil
}
63 changes: 63 additions & 0 deletions rpc/rest/api/rest_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package api

import (
"net/http"
)

import (
"dubbo.apache.org/dubbo-go/v3/common/constant"
restconfig "dubbo.apache.org/dubbo-go/v3/protocol/rest/config"
)

const RestPath = "/api/v1/users/{userID}/greeting"

func InstallProviderRestConfig() {
restconfig.SetRestProviderServiceConfigMap(map[string]*restconfig.RestServiceConfig{
InterfaceName: newRestServiceConfig(constant.DefaultRestServer, ""),
})
}

func InstallConsumerRestConfig() {
restconfig.SetRestConsumerServiceConfigMap(map[string]*restconfig.RestServiceConfig{
InterfaceName: newRestServiceConfig("", constant.DefaultRestClient),
})
}

func newRestServiceConfig(serverType string, clientType string) *restconfig.RestServiceConfig {
return &restconfig.RestServiceConfig{
InterfaceName: InterfaceName,
Server: serverType,
Client: clientType,
RestMethodConfigsMap: map[string]*restconfig.RestMethodConfig{
MethodGetGreeting: {
InterfaceName: InterfaceName,
MethodName: MethodGetGreeting,
Path: RestPath,
MethodType: http.MethodPost,
PathParamsMap: map[int]string{0: "userID"},
QueryParamsMap: map[int]string{1: "name"},
HeadersMap: map[int]string{2: "X-Trace-ID"},
Body: 3,
Consumes: "application/json",
Produces: "application/json",
},
},
}
}
Loading
Loading