This article is more than one year old. Older articles may contain outdated content. Check that the information in the page has not become incorrect since its publication.
Makes it More Convenient for You to Proxy Dubbo Services in Apache APISIX
Background
Apache Dubbo is a MicroService development framework open sourced by Alibaba and donated to Apache, which provides two key capabilities of RPC communication and microservice governance. It has not only been validated by Ali’s massive traffic in e-commerce scenario, but also been widely implemented in domestic technology companies.
In practical application scenarios, Apache Dubbo is generally used as the implementation framework for RPC calls between back-end systems, and when HTTP interfaces need to be provided to the front-end, the Dubbo Service is packaged as an HTTP interface through a “glue layer” and then delivered to the front-end system.
Apache APISIX is the top open source project of Apache Software Foundation and the most active open source gateway project today. As a dynamic, real-time, high-performance open source API gateway, Apache APISIX provides rich traffic management features such as load balancing, dynamic upstream, grayscale publishing, service meltdown, authentication, observability, and more.
Benefiting from the advantages of Apache Dubbo application scenarios, Apache APISIX is based on the open source project tengine/mod_dubbo module to equip Apache Dubbo services with HTTP gateway capabilities. Dubbo Service can be easily published as an HTTP service via the dubbo-proxy plugin.
How to use
Getting Started: Installation and Use
Here we recommend using the Apache APISIX version 2.11 image for installation. This version of APISIX-Base has the Dubbo module compiled by default, so you can use the
dubbo-proxy
plugin directly.
In the next steps, we will use the dubbo-samples
project for a partial demonstration. This project is a demo application implemented using Apache Dubbo, and in this article we use one of the sub-modules as the Dubbo Provider.
Before we get into the action, let’s take a brief look at the definition, configuration, and implementation of the Dubbo interface.
Interface implementation
public interface DemoService {
/**
* standard samples dubbo infterace demo
* @param context pass http infos
* @return Map<String, Object></> pass to response http
**/
Map<String, Object> apisixDubbo(Map<String, Object> httpRequestContext);
}
As shown above, the Dubbo interface is defined in a fixed way. The Map
of method parameters represents the information passed by APISIX to the Dubbo Provider about the HTTP request (e.g. header, body, …). The Map
of the method return value indicates how the Dubbo Provider passes some information to APISIX about the HTTP response to be returned.
After the interface information, the DemoService can be published via XML configuration.
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="org.apache.dubbo.samples.provider.DemoServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="org.apache.dubbo.samples.apisix.DemoService" ref="demoService"/>
After the above configuration, the Consumer can access the apisixDubbo
method through org.apache.dubbo.samples.apisix.DemoService
The specific interface implementation is as follows.
public class DemoServiceImpl implements DemoService {
@Override
public Map<String, Object> apisixDubbo(Map<String, Object> httpRequestContext) {
for (Map.Entry<String, Object> entry : httpRequestContext.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
Map<String, Object> ret = new HashMap<String, Object>();
ret.put("body", "dubbo success\n"); // http response body
ret.put("status", "200"); // http response status
ret.put("test", "123"); // http response header
return ret;
}
}
:::note
In the above code, DemoServiceImpl
prints the received httpRequestContext
and describes the HTTP response to the Dubbo request by returning a Map object with the specified Key.
:::
Operation steps
- Start
dubbo-samples
. - Enable the
dubbo-proxy
plugin in theconfig.yaml
file.
# Add this in config.yaml
plugins:
- ... # plugin you need
- dubbo-proxy
- Create an Upstream that points to the Dubbo Provider.
curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"nodes": {
"127.0.0.1:20880": 1
},
"type": "roundrobin"
}'
- Expose an HTTP route for the DemoService.
curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
"host": "example.org"
"uris": [
"/demo"
],
"plugins": {
"dubbo-proxy": {
"service_name": "org.apache.dubbo.samples.apisix.DemoService",
"service_version": "0.0.0",
"method": "apisixDubbo"
}
},
"upstream_id": 1
}'
- Use the curl command to request Apache APISIX and view the returned results.
curl http://127.0.0.1:9080/demo -H "Host: example.org" -X POST --data '{"name": "hello"}'
< HTTP/1.1 200 OK
< Date: Sun, 26 Dec 2021 11:33:27 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 14
< Connection: keep-alive
< test: 123
< Server: APISIX/2.11.0
<
dubbo success
:::note
The above code returns the test: 123
Header, and the dubbo success
string as the body. This is the same as what we expected in the DemoServiceImpl
code.
:::
- You can view the logs of the Dubbo Provider.
Key = content-length, Value = 17
Key = host, Value = example.org
Key = content-type, Value = application/x-www-form-urlencoded
Key = body, Value = [B@70754265
Key = accept, Value = */*
Key = user-agent, Value = curl/7.80.0
:::note
The Header and Body of the HTTP request are available through the httpRequestContext
, where the Header is used as a Map element, while the Body has a fixed string “body” as the Key value and a Byte array as the Value.
:::
Advanced: Complex Scenario Example
As you can see in the simple use case above, we do publish Dubbo Service as an HTTP service via Apache APISIX, but there are obvious limitations in its use. For example, the parameters and return values of the interface must be Map<String, Object>
.
So, how do you expose the HTTP service through Apache APISIX if there is an interface in your project that is already defined, but does not meet the above restrictions?
Operation steps
For the above scenario, we can use the HTTP Request Body to describe the Service and Method to be invoked and the corresponding parameters, and then use the reflection mechanism of Java to realize the invocation of the target method. Finally, the return value is serialized to JSON and written to the HTTP Response Body.
This will further enhance the “HTTP to Dubbo” capability of Apache APISIX and apply it to all existing Dubbo services. For details, see the following.
- Add a Dubbo Service for existing projects to handle HTTP to Dubbo conversions in a unified way.
public class DubboInvocationParameter {
private String type;
private String value;
}
public class DubboInvocation {
private String service;
private String method;
private DubboInvocationParameter[] parameters;
}
public interface HTTP2DubboService {
Map<String, Object> invoke(Map<String, Object> context) throws Exception;
}
@Component
public class HTTP2DubboServiceImpl implements HTTP2DubboService {
@Autowired
private ApplicationContext appContext;
@Override
public Map<String, Object> invoke(Map<String, Object> context) throws Exception {
DubboInvocation invocation = JSONObject.parseObject((byte[]) context.get("body"), DubboInvocation.class);
Object[] args = new Object[invocation.getParameters().size()];
for (int i = 0; i < args.length; i++) {
DubboInvocationParameter parameter = invocation.getParameters().get(i);
args[i] = JSONObject.parseObject(parameter.getValue(), Class.forName(parameter.getType()));
}
Object svc = appContext.getBean(Class.forName(invocation.getService()));
Object result = svc.getClass().getMethod(invocation.getMethod()).invoke(args);
Map<String, Object> httpResponse = new HashMap<>();
httpResponse.put("status", 200);
httpResponse.put("body", JSONObject.toJSONString(result));
return httpResponse;
}
}
- Initiate the relevant call with the following command request.
curl http://127.0.0.1:9080/demo -H "Host: example.org" -X POST --data '
{
"service": "org.apache.dubbo.samples.apisix.DemoService",
"method": "createUser",
"parameters": [
{
"type": "org.apache.dubbo.samples.apisix.User",
"value": "{'name': 'hello'}"
}
]
}'
Summary
In this article, we introduced how to use Apache APISIX to implement a proxy for Dubbo Service. By introducing the dubbo-proxy
plugin, you can build a simpler and more efficient traffic link for the back-end system of Dubbo framework.
We hope that the above steps and use cases will provide you with ideas for using it in relevant scenarios. For more information about the dubbo-proxy
plugin, please refer to the official documentation.