當後端Java服務用Dubbo協議做爲RPC方案的基礎,但部分消費方是前端Restful的PHP服務,不能直接調用,因而在中間架設了Router服務提供統一的基於HTTP的後端調用入口。
而Router調用後端Java服務就應用了Dubbo的高級特性--泛化調用前端
本文將用一個小Demo來演示上面所述的泛化調用應用場景java
DUBBO是一個分佈式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,是阿里巴巴SOA服務化治理方案的核心框架,天天爲2,000+個服務提供3,000,000,000+次訪問量支持,並被普遍應用於阿里巴巴集團的各成員站點。
-- Dubbo官方描述webDubbo能作什麼:
-- 《Dubbo功能介紹》(官方資料)spring
注:Dubbo的基本使用介紹不在本文範疇,若有須要請自行參考官方資料編程
泛接口調用方式主要用於客戶端沒有API接口及模型類元的狀況,參數及返回值中的全部POJO均用Map表示,一般用於框架集成,好比:實現一個通用的服務測試框架,可經過GenericService調用全部服務實現。
-- Dubbo用戶指南json
public interface UserInfoService { public Map<String, String> getUser(String id); public Map<String, String>[] getUsers(); }
dubboconf.properties:
後端
application.name=router registry.address=zookeeper://address1?buckup=address2,address3
{ "interfaceName": "foo", "methodName": "bar", "methodParams": [ { "id": "xxx" } ] }
RequestDto.java:
api
import java.util.Map; /** * Created by Luo */ public class RequestDto { private String interfaceName; private String methodName private Map[] methodParams; public String getInterfaceName() { return interfaceName; } public void setInterfaceName(String interfaceName) { this.interfaceName = interfaceName; } public String getMethodName() { return methodName; } public void setMethodName(String methodName) { this.methodName = methodName; } public Map[] getMethodParams() { return methodParams; } public void setMethodParam(Map[] methodParams) { this.methodParams = methodParams; } }
RouterController.java:
緩存
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by Luo */ @RestController public class App { @RequestMapping(value = "/router/", method = RequestMethod.POST) public Object getUser(@ModelAttribute RequestDto dto) { Map map = new HashMap<>(); map.put("ParamType", "java.lang.String"); //後端接口參數類型 map.put("Object", dto.getMethodParams()[0].get("id")); //用以調用後端接口的實參 List<Map<String, Object>> paramInfos= new ArrayList<>(); paramInfos.add(map); DubboServiceFactory dubbo = DubboServiceFactory.getInstance(); return dubbo.genericInvoke(dto.getInterfaceName(), dto.getMethodName(), paramInfos); } }
注:本文旨在演示泛化調用的一種應用方式,爲簡明起見,代碼中直接從dto中獲取了指定參數,而並無完整實現其路由功能,望見諒。app
DubboServiceFactory.java
package local.demo.genericservice; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.utils.ReferenceConfigCache; import com.alibaba.dubbo.rpc.service.GenericService; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Properties; /** * Created by Luo */ public class DubboServiceFactory { private ApplicationConfig application; private RegistryConfig registry; private static class SingletonHolder { private static DubboServiceFactory INSTANCE = new DubboServiceFactory(); } private DubboServiceFactory(){ Properties prop = new Properties(); ClassLoader loader = DubboServiceFactory.class.getClassLoader(); try { prop.load(loader.getResourceAsStream("dubboconf.properties")); } catch (IOException e) { e.printStackTrace(); } ApplicationConfig applicationConfig = new ApplicationConfig(); applicationConfig.setName(prop.getProperty("application.name")); //這裏配置了dubbo的application信息*(demo只配置了name)*,所以demo沒有額外的dubbo.xml配置文件 RegistryConfig registryConfig = new RegistryConfig(); registryConfig.setAddress(prop.getProperty("registry.address")); //這裏配置dubbo的註冊中心信息,所以demo沒有額外的dubbo.xml配置文件 this.application = applicationConfig; this.registry = registryConfig; } public static DubboServiceFactory getInstance() { return SingletonHolder.INSTANCE; } public Object genericInvoke(String interfaceClass, String methodName, List<Map<String, Object>> parameters){ ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); reference.setApplication(application); reference.setRegistry(registry); reference.setInterface(interfaceClass); // 接口名 reference.setGeneric(true); // 聲明爲泛化接口 //ReferenceConfig實例很重,封裝了與註冊中心的鏈接以及與提供者的鏈接, //須要緩存,不然重複生成ReferenceConfig可能形成性能問題而且會有內存和鏈接泄漏。 //API方式編程時,容易忽略此問題。 //這裏使用dubbo內置的簡單緩存工具類進行緩存 ReferenceConfigCache cache = ReferenceConfigCache.getCache(); GenericService genericService = cache.get(reference); // 用com.alibaba.dubbo.rpc.service.GenericService能夠替代全部接口引用 int len = parameters.size(); String[] invokeParamTyeps = new String[len]; Object[] invokeParams = new Object[len]; for(int i = 0; i < len; i++){ invokeParamTyeps[i] = parameters.get(i).get("ParamType") + ""; invokeParams[i] = parameters.get(i).get("Object"); } return genericService.$invoke(methodName, invokeParamTyeps, invokeParams); } }
將Router部署到Jetty/Tomcat等容器,或者直接使用SpringBoot開發,發佈爲內嵌Jetty/Tomcat的獨立jar包,便可向前端服務提供服務。