Dubbo 高級特性實踐-泛化調用


引言

當后端Java服務用Dubbo協議作為RPC方案的基礎,但部分消費方是前端Restful的PHP服務,不能直接調用,於是在中間架設了Router服務提供統一的基於HTTP的后端調用入口。

而Router調用后端Java服務就應用了Dubbo的高級特性–泛化調用

  • 直接消費方(Router服務)不需要引入接口jar包
  • 通過GenericService接口來處理所有服務請求
  • 以PHP到Router的request body中的方法名和方法參數作為Router遠程調用后端Java服務的入參,最后將遠程調用的result返回給PHP端

本文將用一個小Demo來演示上面所述的泛化調用應用場景

零.Dubbo簡介

DUBBO是一個分布式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,是阿里巴巴SOA服務化治理方案的核心框架,每天為2,000+個服務提供3,000,000,000+次訪問量支持,並被廣泛應用於阿里巴巴集團的各成員站點。

– Dubbo官方描述

Dubbo能做什么:

①、透明化的遠程方法調用

就像調用本地方法一樣調用遠程方法

只需簡單配置,沒有任何API侵入。

②、軟負載均衡及容錯機制

可在內網替代F5等硬件負載均衡器

③、服務自動注冊與發現

不再需要寫死服務提供方地址,注冊中心基於接口名查詢服務提 供者的IP地址,並且能夠平滑添加或刪除服務提供者

– 《Dubbo功能介紹》(官方資料)

注:Dubbo的基本使用介紹不在本文范疇,如有需要請自行參考官方資料

泛接口調用方式主要用於客戶端沒有API接口及模型類元的情況,參數及返回值中的所有POJO均用Map表示,通常用於框架集成,比如:實現一個通用的服務測試框架,可通過GenericService調用所有服務實現。

– Dubbo用戶指南

一、后端API

public interface UserInfoService {
public Map<String, String> getUser(String id);
public Map<String, String>[] getUsers();
}

二、Router端dubbo配置

dubboconf.properties:
application.name=router
registry.address=zookeeper://address1?buckup=address2,address3

三、前端服務post到Router的Request Body示例:

{
"interfaceName": "foo",
"methodName": "bar",
"methodParams": [
{
"id": "xxx"
}
]
}

四、處理前端參數用的Dto

RequestDto.java:

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;
}
}

五、Router服務入口

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中獲取了指定參數,而並沒有完整實現其路由功能,望見諒。

六、通過GenericService進行泛化調用

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包,即可向前端服務提供服務。

寫在最后:歡迎留言討論,加關注,持續更新!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM