Publishing REST-style Services

Demonstrates how to access services published via triple and dubbo protocols using standard rest requests.

When discussing the triple protocol example, we mentioned that the triple protocol supports direct access in application/json format:

curl \
    --header "Content-Type: application/json" \
    --data '["Dubbo"]' \
    http://localhost:50052/org.apache.dubbo.samples.api.GreetingsService/sayHi/

If you find the above http://localhost:50052/org.apache.dubbo.samples.api.GreetingsService/sayHi format of path request not user-friendly, you can customize HTTP request paths and methods through annotations. Currently, three annotation formats are supported: built-in, Spring Web, and JAX-RS. For the complete code of the following example, refer to dubbo-samples-triple-rest.

Download and Run the Example

# Get the example code
git clone --depth=1 https://github.com/apache/dubbo-samples.git
cd dubbo-samples/2-advanced/dubbo-samples-triple-rest/dubbo-samples-triple-rest-basic
# Run directly
mvn spring-boot:run
# Or package and run
mvn clean package -DskipTests
java -jar target/dubbo-samples-triple-rest-basic-1.0.0-SNAPSHOT.jar

Of course, you can also import the project directly with an IDE and execute org.apache.dubbo.rest.demo.BasicRestApplication#main to run it, and use breakpoints to debug and deepen your understanding of the principles.

Example Code

// Service Interface
package org.apache.dubbo.rest.demo;

import org.apache.dubbo.remoting.http12.rest.Mapping;
import org.apache.dubbo.remoting.http12.rest.Param;

public interface DemoService {
    String hello(String name);

    @Mapping(path = "/hi", method = HttpMethods.POST)
    String hello(User user, @Param(value = "c", type = ParamType.Header) int count);
}

// Service Implementation
@DubboService
public class DemoServiceImpl implements DemoService {
    @Override
    public String hello(String name) {
        return "Hello " + name;
    }

    @Override
    public String hello(User user, int count) {
        return "Hello " + user.getTitle() + ". " + user.getName() + ", " + count;
    }
}

// Model
@Data
public class User {
    private String title;
    private String name;
}

Testing the Basic Service

curl -v "http://127.0.0.1:8081/org.apache.dubbo.rest.demo.DemoService/hello?name=world"
# Output looks like
#> GET /org.apache.dubbo.rest.demo.DemoService/hello?name=world HTTP/1.1
#> Host: 127.0.0.1:8081
#> User-Agent: curl/8.7.1
#> Accept: */*
#>
#* Request completely sent off
#< HTTP/1.1 200 OK
#< content-type: application/json
#< alt-svc: h2=":8081"
#< content-length: 13
#<
#"Hello world"

Code Explanation:
You can see “Hello world” was outputted, and the double quotes indicate the default output content-type was application/json.
From this example, you can understand that Triple exports the service to /{serviceInterface}/{methodName} path by default and supports passing parameters through the URL.

Testing the Advanced Service

curl -v -H "c: 3" -d 'name=Yang' "http://127.0.0.1:8081/org.apache.dubbo.rest.demo.DemoService/hi.txt?title=Mr"
# Output looks like
#> POST /org.apache.dubbo.rest.demo.DemoService/hi.txt?title=Mr HTTP/1.1
#> Host: 127.0.0.1:8081
#> User-Agent: curl/8.7.1
#> Accept: */*
#> c: 3
#> Content-Length: 9
#> Content-Type: application/x-www-form-urlencoded
#>
#* upload completely sent off: 9 bytes
#< HTTP/1.1 200 OK
#< content-type: text/plain
#< alt-svc: h2=":8081"
#< content-length: 17
#<
#Hello Mr. Yang, 3

Code Explanation:
The output “Hello Mr. Yang, 3” shows no double quotes because the output is requested as text/plain by specifying the suffix txt.
This example illustrates how to customize paths via the Mapping annotation and customize parameter sources via the Param annotation, supporting parameters passed through the post body or URL; for detailed instructions, see: Basic User Guide

Observing Logs

You can open debug logs to understand the startup and response request process of REST.

logging:
  level:
    "org.apache.dubbo.rpc.protocol.tri": debug
    "org.apache.dubbo.remoting": debug

Once enabled, you can observe the Rest mapping registration and request-response process.

# Register mapping
DEBUG o.a.d.r.p.t.TripleProtocol               :  [DUBBO] Register triple grpc mapping: 'org.apache.dubbo.rest.demo.DemoService' -> invoker[tri://192.168.2.216:8081/org.apache.dubbo.rest.demo.DemoService]
 INFO .r.p.t.r.m.DefaultRequestMappingRegistry :  [DUBBO] BasicRequestMappingResolver resolving rest mappings for ServiceMeta{interface=org.apache.dubbo.rest.demo.DemoService, service=DemoServiceImpl@2a8f6e6} at url [tri://192.168.2.216:8081/org.apache.dubbo.rest.demo.DemoService]
DEBUG .r.p.t.r.m.DefaultRequestMappingRegistry :  [DUBBO] Register rest mapping: '/org.apache.dubbo.rest.demo.DemoService/hi' -> mapping=RequestMapping{name='DemoServiceImpl#hello', path=PathCondition{paths=[org.apache.dubbo.rest.demo.DemoService/hi]}, methods=MethodsCondition{methods=[POST]}}, method=MethodMeta{method=org.apache.dubbo.rest.demo.DemoService.hello(User, int), service=DemoServiceImpl@2a8f6e6}
DEBUG .r.p.t.r.m.DefaultRequestMappingRegistry :  [DUBBO] Register rest mapping: '/org.apache.dubbo.rest.demo.DemoService/hello' -> mapping=RequestMapping{name='DemoServiceImpl#hello~S', path=PathCondition{paths=[org.apache.dubbo.rest.demo.DemoService/hello]}}, method=MethodMeta{method=org.apache.dubbo.rest.demo.DemoService.hello(String), service=DemoServiceImpl@2a8f6e6}
 INFO .r.p.t.r.m.DefaultRequestMappingRegistry :  [DUBBO] Registered 2 REST mappings for service [DemoServiceImpl@44627686] at url [tri://192.168.2.216:8081/org.apache.dubbo.rest.demo.DemoService] in 11ms

# Request Response
DEBUG .a.d.r.p.t.r.m.RestRequestHandlerMapping :  [DUBBO] Received http request: DefaultHttpRequest{method='POST', uri='/org.apache.dubbo.rest.demo.DemoService/hi.txt?title=Mr', contentType='application/x-www-form-urlencoded'}
DEBUG .r.p.t.r.m.DefaultRequestMappingRegistry :  [DUBBO] Matched rest mapping=RequestMapping{name='DemoServiceImpl#hello', path=PathCondition{paths=[/org.apache.dubbo.rest.demo.DemoService/hi]}, methods=MethodsCondition{methods=[POST]}}, method=MethodMeta{method=org.apache.dubbo.rest.demo.DemoService.hello(User, int), service=DemoServiceImpl@2a8f6e6}
DEBUG .a.d.r.p.t.r.m.RestRequestHandlerMapping :  [DUBBO] Content-type negotiate result: request='application/x-www-form-urlencoded', response='text/plain'
DEBUG .d.r.h.AbstractServerHttpChannelObserver :  [DUBBO] Http response body is: '"Hello Mr. Yang, 3"'
DEBUG .d.r.h.AbstractServerHttpChannelObserver :  [DUBBO] Http response headers sent: {:status=[200], content-type=[text/plain], alt-svc=[h2=":8081"], content-length=[17]}

Practical Application Scenarios

Next, let’s look at what real problems can be solved with triple protocol supporting REST format access.

Spring Cloud Interoperation

First, the initial scenario is to enable interoperability between the Dubbo system and HTTP microservice systems.

Imagine you are in charge of a business line, and you have a microservice cluster developed based on Dubbo, where services communicate using the triple binary protocol. There is another important business within the company running on a microservice cluster developed based on Spring Cloud, where the services communicate using HTTP+JSON. Now you want to enable communication between these two businesses; how can services interact? The triple protocol supporting REST format access can solve this problem, allowing the Dubbo microservice cluster to communicate internally using the triple binary protocol while externally using the REST request format provided by triple.

Gateway Traffic Access

Another very valuable scenario for supporting REST format access is that it facilitates gateway traffic access. Accessing binary-format RPC protocols has always been a challenge. Previously, Dubbo provided generic calls to solve this problem; gateways could implement HTTP -> Dubbo protocol conversion to access backend microservice clusters.

Now, with support for REST format, decentralized access can be achieved without any gateway protocol conversion. For more details, see HTTP Gateway Traffic Access.