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

引言

當後端Java服務用Dubbo協議做爲RPC方案的基礎,但部分消費方是前端Restful的PHP服務,不能直接調用,因而在中間架設了Router服務提供統一的基於HTTP的後端調用入口。
而Router調用後端Java服務就應用了Dubbo的高級特性--泛化調用前端

  • 直接消費方(Router服務)不須要引入接口jar包
  • 經過GenericService接口來處理全部服務請求
  • 以PHP到Router的request body中的方法名和方法參數做爲Router遠程調用後端Java服務的入參,最後將遠程調用的result返回給PHP端

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

零.Dubbo簡介

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

Dubbo能作什麼:

  • 透明化的遠程方法調用
    • 就像調用本地方法同樣調用遠程方法
    • 只需簡單配置,沒有任何API侵入。
  • 軟負載均衡及容錯機制
    • 可在內網替代F5等硬件負載均衡器
  • 服務自動註冊與發現
    • 再也不須要寫死服務提供方地址,註冊中心基於接口名查詢服務提 供者的IP地址,而且可以平滑添加或刪除服務提供者

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

注:Dubbo的基本使用介紹不在本文範疇,若有須要請自行參考官方資料編程

泛接口調用方式主要用於客戶端沒有API接口及模型類元的狀況,參數及返回值中的全部POJO均用Map表示,一般用於框架集成,好比:實現一個通用的服務測試框架,可經過GenericService調用全部服務實現。
-- Dubbo用戶指南json

一.後端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: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;
    }
}

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

六.經過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包,便可向前端服務提供服務。

相關文章
相關標籤/搜索