为什么使用网关 Dubbo服务本身没有暴露HTTP接口,客户端(如:Web,APP)无法直接调用其提供的方法。 而APISIX 可以通过dubbo-proxy插件 为Dubbo服务提供外部访问的HTTP接口,因此特别适合与Dubbo框架一起使用。
在Dubbo服务架构中如何使用APISIX 关于在Dubbo服务架构中使用APISIX作为接入网关,Dubbo官方的文档 已经给出了说明。 在此,结合具体的示例进行实践。
假设已经存在了基于Dubbo架构的服务提供者和消费者,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public interface HelloService { String sayHello (String name) ; } @DubboService public class HelloServiceImpl implements HelloService { @Override public String sayHello (String name) { return "Hello, " + name; } } public interface HelloServiceConsumer { Object hello (String name) ; } @Service public class HelloServiceConsumerImpl implements HelloServiceConsumer { @DubboReference private HelloService helloService; @Override public Object hello (String name) { return this .helloService.sayHello(name); } }
此时如果希望Dubbo服务消费者(也可以是任何Dubbo服务)希望被来自APISIX的HTTP请求调用,那么如何实现呢?
按照dubbo-proxy插件 文档示例描述,APISIX路由中指定的插件参数包含3个部分:
1 2 3 "service_name": "org.apache.dubbo.sample.tengine.DemoService", # 要调用的Dubbo服务接口完整限定名 "service_version": "0.0.0", # Dubbo服务版本名称,默认为0.0.0 "method": "tengineDubbo" # Dubbo服务方法名
也就是说,必须存在一个Dubbo服务接口org.apache.dubbo.sample.tengine.DemoService
,该接口有一个名为tengineDubbo
的方法。
1 2 3 4 5 6 7 8 9 10 11 12 public interface DemoService { Map<String, Object> tengineDubbo (Map<String, Object> context) ; }
如果此时向APISIX的路由接口发起一个请求:
1 2 3 4 5 6 7 8 9 10 11 curl http://127.0.0.1:9080/demo -H "Host: example.org" -X POST --data ' { "service": "org.chench.extra.dubbo.consumer.service.HelloServiceConsumer", "method": "hello", "parameters": [ { "type": "java.lang.String", "value": "chench" } ] }'
那么,消息体中的内容就是传递给Dubbo服务方法的Map对象数据。
所以,为了调用任意的Dubbo服务,应该编写一个专门对接APISIX请求的胶水层
Dubbo服务,在该服务方法中通过反射的方式实现对任意其他Dubbo服务的调用。 如下接口或实现类都定义在Java包org.chench.extra.dubbo.apisix
中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class DubboInvocation { private String service; private String method; private DubboInvocationParameter[] parameters; } public class DubboInvocationParameter { private String type; private String value; } public interface APISIX2DubboService { Map<String, Object> invoke (Map<String, Object> context) throws Exception; } public class APISIX2DubboServiceImpl implements APISIX2DubboService { private static final Logger LOGGER = LoggerFactory.getLogger(APISIX2DubboServiceImpl.class); @Autowired private ApplicationContext appContext; @Override public Map<String, Object> invoke (Map<String, Object> context) throws Exception { Map<String, Object> httpResp = new HashMap <>(); String body = null ; try { body = new String ((byte []) context.get("body" )); DubboInvocation invocation = JSONObject.parseObject(body, DubboInvocation.class); int paramSize = invocation.getParameters().length; Object[] args = new Object [paramSize]; Class[] types = new Class [paramSize]; for (int i = 0 ; i < args.length; i++) { DubboInvocationParameter parameter = invocation.getParameters()[i]; args[i] = parameter.getValue(); types[i] = Class.forName(parameter.getType()); } Object svc = appContext.getBean(Class.forName(invocation.getService())); Object result = svc.getClass().getMethod(invocation.getMethod(), types).invoke(svc, args); httpResp.put("status" , 200 ); httpResp.put("body" , JSONObject.toJSONString(result)); } catch (Exception e) { LOGGER.error("invoke dubbo error, body: {}, error: {}" , body, e.getMessage(), e); httpResp.put("status" , 500 ); httpResp.put("body" , e.getStackTrace()); e.printStackTrace(); } return httpResp; } }
然后将这个对接APISIX的Dubbo服务进行注册:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd" > <bean id ="APISIX2DubboService" class ="org.chench.extra.dubbo.apisix.APISIX2DubboServiceImpl" /> <dubbo:service interface ="org.chench.extra.dubbo.apisix.APISIX2DubboService" ref ="APISIX2DubboService" /> </beans >
至此,在Dubbo服务侧需要准备的工作就完毕了。
在APISIX这一侧,需要做2件事情: 第一,启用dubbo-proxy
插件。 如果是以Docker方式启动的APISIX,在配置文件${APISIX_HOME}/example/apisix_conf/config.yaml
中添加插件配置。
1 2 3 plugins: - dubbo-proxy
第二,创建一个路由指定需要调用的Dubbo服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 curl http://127.0.0.1:9180/apisix/admin/routes/2 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uris": [ "/demo" ], "plugins": { "dubbo-proxy": { "service_name": "org.chench.extra.dubbo.apisix.APISIX2DubboService", # 这是对接APISIX的Dubbo服务接口 "service_version": "0.0.0", "method": "invoke" } }, "upstream": { "type": "roundrobin", "nodes": { "192.168.2.8:20881": 1 } } }'
最后,调用APISIX接口进行验证:
1 2 3 4 5 6 7 8 9 10 11 curl http://127.0.0.1:9080/demo -X POST --data ' { "service": "org.chench.extra.dubbo.consumer.service.HelloServiceConsumer", # 指定需要调用的Dubbo服务接口完整限定名 "method": "hello", # 指定需要调用的Dubbo服务接口方法 "parameters": [ # 指定需要调用的Dubbo服务接口方法参数列表,每一个参数都要指定类型和值 { "type": "java.lang.String", "value": "chench" } ] }' -D-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # 调用成功返回 HTTP/1.1 200 OK Date: Mon, 07 Aug 2023 15:17:24 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 15 Connection: keep-alive Server: APISIX/3.4.1 "Hello, chench" # 调用失败返回 HTTP/1.1 502 Bad Gateway Date: Mon, 07 Aug 2023 15:18:09 GMT Content-Type: text/html; charset=utf-8 Content-Length: 229 Connection: keep-alive Server: APISIX/3.4.1 X-APISIX-Upstream-Status: 502 <html> <head><title>502 Bad Gateway</title></head> <body> <center><h1>502 Bad Gateway</h1></center> <hr><center>openresty</center> <p><em>Powered by <a href="https://apisix.apache.org/">APISIX</a>.</em></p></body> </html>
至此,一个以APISIX作为网关调用Dubbo服务的方法就实现了。
但是如果在每一个Dubbo架构的应用中都需要定义一个对接APISIX的胶水层
Dubbo服务,显得重复而且不利用维护,所以可以将这个胶水层
Dubbo服务单独做成一个Spring Boot Starter组件,然后在每一个需要使用的地方直接引入即可,具体实现参考:dubbo-apisix-springboot-starter 。
【参考】APISIX+Dubbo+Nacos 最佳实践 Dubbo注解方式与spring的整合原理即@DubboService的机制(2) 【Dubbo】三种Dubbo配置与实现原理(XML、注解、API)