很多小伙伴在測試的時候發(fā)現(xiàn)使用現(xiàn)成的工具無法對dubbo協(xié)議進(jìn)行調(diào)用,我們現(xiàn)在就來看一下如何通過封裝馋缅,將dubbo封裝成一個http協(xié)議扒腕,然后再使用現(xiàn)成的http測試工具即可實現(xiàn)dubbo的直接調(diào)用。
首先萤悴,我們來看dubbo是啥
Dubbo是Alibaba開源的分布式服務(wù)框架瘾腰,它最大的特點是按照分層的方式來架構(gòu),使用這種方式可以使各個層之間解耦合(或者最大限度地松耦合)覆履。從服務(wù)模型的角度來看蹋盆,Dubbo采用的是一種非常簡單的模型,要么是提供方提供服務(wù)硝全,要么是消費方消費服務(wù)栖雾,所以基于這一點可以抽象出服務(wù)提供方(Provider)和服務(wù)消費方(Consumer)兩個角色。關(guān)于注冊中心伟众、協(xié)議支持析藕、服務(wù)監(jiān)控等內(nèi)容,詳見后面描述凳厢。
總體架構(gòu)
Dubbo的總體架構(gòu)账胧,如圖所示:
Dubbo框架設(shè)計一共劃分了10個層,而最上面的Service層是留給實際想要使用Dubbo開發(fā)分布式服務(wù)的開發(fā)者實現(xiàn)業(yè)務(wù)邏輯的接口層先紫。圖中左邊淡藍(lán)背景的為服務(wù)消費方使用的接口治泥,右邊淡綠色背景的為服務(wù)提供方使用的接口, 位于中軸線上的為雙方都用到的接口遮精。
下面居夹,結(jié)合Dubbo官方文檔,我們分別理解一下框架分層架構(gòu)中,各個層次的設(shè)計要點:
服務(wù)接口層(Service):該層是與實際業(yè)務(wù)邏輯相關(guān)的准脂,根據(jù)服務(wù)提供方和服務(wù)消費方的業(yè)務(wù)設(shè)計對應(yīng)的接口和實現(xiàn)劫扒。
配置層(Config):對外配置接口,以ServiceConfig和ReferenceConfig為中心意狠,可以直接new配置類粟关,也可以通過spring解析配置生成配置類。
服務(wù)代理層(Proxy):服務(wù)接口透明代理环戈,生成服務(wù)的客戶端Stub和服務(wù)器端Skeleton,以ServiceProxy為中心澎灸,擴展接口為ProxyFactory院塞。
服務(wù)注冊層(Registry):封裝服務(wù)地址的注冊與發(fā)現(xiàn),以服務(wù)URL為中心性昭,擴展接口為RegistryFactory拦止、Registry和RegistryService∶拥撸可能沒有服務(wù)注冊中心汹族,此時服務(wù)提供方直接暴露服務(wù)。
集群層(Cluster):封裝多個提供者的路由及負(fù)載均衡其兴,并橋接注冊中心顶瞒,以Invoker為中心,擴展接口為Cluster元旬、Directory榴徐、Router和LoadBalance。將多個服務(wù)提供方組合為一個服務(wù)提供方匀归,實現(xiàn)對服務(wù)消費方來透明坑资,只需要與一個服務(wù)提供方進(jìn)行交互。
監(jiān)控層(Monitor):RPC調(diào)用次數(shù)和調(diào)用時間監(jiān)控穆端,以Statistics為中心袱贮,擴展接口為MonitorFactory、Monitor和MonitorService体啰。
遠(yuǎn)程調(diào)用層(Protocol):封將RPC調(diào)用攒巍,以Invocation和Result為中心,擴展接口為Protocol狡赐、Invoker和Exporter窑业。Protocol是服務(wù)域,它是Invoker暴露和引用的主功能入口枕屉,它負(fù)責(zé)Invoker的生命周期管理常柄。Invoker是實體域,它是Dubbo的核心模型,其它模型都向它靠擾西潘,或轉(zhuǎn)換成它卷玉,它代表一個可執(zhí)行體,可向它發(fā)起invoke調(diào)用喷市,它有可能是一個本地的實現(xiàn)相种,也可能是一個遠(yuǎn)程的實現(xiàn),也可能一個集群實現(xiàn)品姓。
信息交換層(Exchange):封裝請求響應(yīng)模式寝并,同步轉(zhuǎn)異步,以Request和Response為中心腹备,擴展接口為Exchanger衬潦、ExchangeChannel、ExchangeClient和ExchangeServer植酥。
網(wǎng)絡(luò)傳輸層(Transport):抽象mina和netty為統(tǒng)一接口镀岛,以Message為中心,擴展接口為Channel友驮、Transporter漂羊、Client、Server和Codec卸留。
數(shù)據(jù)序列化層(Serialize):可復(fù)用的一些工具走越,擴展接口為Serialization、 ObjectInput艾猜、ObjectOutput和ThreadPool买喧。
上面的介紹看不懂?沒關(guān)系匆赃,我也是從別人的論壇抄的淤毛,它就是一個分布式的協(xié)議架構(gòu),解決企業(yè)級應(yīng)用問題算柳,學(xué)dubbo之路很漫長低淡,涉及到多種協(xié)議、網(wǎng)絡(luò)知識瞬项、zookeeper蔗蹋、spring,我們統(tǒng)統(tǒng)先不管它們囱淋,直接開始將它封裝成我們熟悉的http協(xié)議猪杭。
預(yù)警:以下的代碼不是dubbo的常規(guī)用法,是小編從dubbo的spring架構(gòu)里拆出來的函數(shù)妥衣,做做工具還可以皂吮,不要拿來學(xué)做企業(yè)級應(yīng)用戒傻,否則你會越走越遠(yuǎn)。為了工具更輕量級蜂筹,我們只好將它從spring里抽離出來(小編搞了好久才完成抽離需纳,是不是應(yīng)該給個紅包)
啥?你想學(xué)常規(guī)版本的dubbo應(yīng)用艺挪,建議你還是去網(wǎng)上看dubbo教程吧不翩,出門左拐。
我們開始正式的教程麻裳。
首先口蝠,你要有個dubbo的jar包,這一點alibaba做的很好津坑,將所有dubbo需要的類封裝成了一個jar亚皂,中國平安的maven可以下載到(當(dāng)然其他的網(wǎng)站也可以):
http://maven.pingan.work/nexus/service/local/repositories/central/content/com/alibaba/dubbo/2.0.10/dubbo-2.0.10.jar
maven依賴是(如果在spring里整合出現(xiàn)jar沖突,請使用exclusion規(guī)避):
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.4.10</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
接下來直接上代碼:
首先国瓮,我們先上一個springmvc的controller來封裝http,具體的配置請按照標(biāo)準(zhǔn)springmvc來封裝狞谱,或者你也可以使用sevlet:
@Controller
@RequestMapping(value = "/bsp", produces = "application/json; charset=utf8")
public class CallBspController extends BaseController {
/**
* @Description: 調(diào)用bsp
* @author: dingjingjing058
* @date: 2016年7月4日
*/
@RequestMapping(value = "/callbsp.do", method = RequestMethod.GET, produces = "text/html;charset=UTF-8")
@ResponseBody
public String callBsp(@ModelAttribute BspForm bspForm, HttpSession httpSession) {
if (bspForm.getDubboUrl() == null || bspForm.getDubboAdress()== null) {
return new ResponseUtil<>(CodeEnum.PARAFORMSTMISS).toJSONString();
}
CallBspServiceImpl callBspServiceImpl=new CallBspServiceImpl();
return callBspServiceImpl.callBsp(bspForm).toJSONString();
}
}
http傳入的form如下:
package com.pingan.testcloud.form;
/**
* ClassName:UserForm
* @Description: bsp表單類
* @author dingjingjing058
* @date 2016年7月4日
*/
public class BspForm {
private String dubboAdress;
private String dubboUrl;
private String paraMeters;
/**
* @return the dubboUrl
*/
public String getDubboUrl() {
return dubboUrl;
}
/**
* @param dubboUrl the dubboUrl to set
*/
public void setDubboUrl(String dubboUrl) {
this.dubboUrl = dubboUrl;
}
/**
* @return the paraMeters
*/
public String getParaMeters() {
return paraMeters;
}
/**
* @param paraMeters the paraMeters to set
*/
public void setParaMeters(String paraMeters) {
this.paraMeters = paraMeters;
}
/**
* @return the dubboAdress
*/
public String getDubboAdress() {
return dubboAdress;
}
/**
* @param dubboAdress the dubboAdress to set
*/
public void setDubboAdress(String dubboAdress) {
this.dubboAdress = dubboAdress;
}
}
下面來封裝dubbo乃摹,我們采用直接點對點rpc調(diào)用,不走dubbo注冊中心:
package com.pingan.testcloud.service.impl;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import com.paic.pafa.ac.dubbo.GenericParam;
import com.paic.pafa.ac.dubbo.GenericResult;
import com.pingan.testcloud.form.BspForm;
import com.pingan.testcloud.service.CallBspService;
import com.pingan.testcloud.units.CodeEnum;
import com.pingan.testcloud.units.ResponseUtil;
public class CallBspServiceImpl implements CallBspService {
@Override
public ResponseUtil<?> callBsp(BspForm bspForm) {
//try {
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();//獲取服務(wù)跟衅,注意GenericService是一個范類型孵睬,可以替代服務(wù)端任意類
reference.setUrl("dubbo://" + bspForm.getDubboAdress() + "/" + bspForm.getDubboUrl()); //設(shè)置dubbo服務(wù)端地址和端口號
reference.setInterface(bspForm.getDubboUrl()); //設(shè)置dubbo服務(wù)地址
reference.setGeneric(true);
reference.setApplication(new ApplicationConfig("ff-test"));
reference.setTimeout(10000);
// reference.setRegistry(new
// RegistryConfig("zookeeper://10.21.66.48:2181"));//如果你要走zookeep管控中心,就使用它
GenericService genericService = reference.get();//獲取服務(wù)
GenericParam param = new GenericParam();
param.setParams(getParams(bspForm.getParaMeters()));
GenericResult result = (GenericResult) genericService.$invoke("anymethod",
new String[] {"com.paic.pafa.ac.dubbo.GenericParam"}, new Object[] { param }); //調(diào)用遠(yuǎn)程類的方法
reference.destroy();//這里一定要用伶跷,否則dubbo會一直連接掰读,沒幾十次調(diào)用系統(tǒng)就會掛掉
return new ResponseUtil<>(CodeEnum.SUCCESS, result); //打印結(jié)果,ResponseUtil的封裝見附件
//}
//catch (Exception e) {
// return new ResponseUtil<>(CodeEnum.FAILURE);
//}
}
public Map<String, Object> getParams(String dubboparaMeters) {
String[] paraMeters={};
if(dubboparaMeters!=null)
paraMeters = dubboparaMeters.split(",");
Map<String, Object> params = new HashMap<String, Object>();
Map<String, Object> esbRequest = new HashMap<String, Object>();
Map<String, Object> header = new HashMap<String, Object>();
Map<String, Object> content = new HashMap<String, Object>();
esbRequest.put("content", content);
esbRequest.put("header", header);
String sendSerialNo = this.getSeqNo();
esbRequest.put("sendSerialNo", sendSerialNo);
if(paraMeters!=null)
for (String para : paraMeters) {
String[] paraKeyValues = para.split(":");
esbRequest.put(paraKeyValues[0], paraKeyValues[1]);
}
params.put("esbRequest", esbRequest);
params.put("timeOutMs", "30000");
params.put("systemId", "958537");
params.put("requestNo", sendSerialNo);
return params;
}
public String getSeqNo() {
return String.valueOf(System.currentTimeMillis() + System.nanoTime() + new Random().nextInt(2));
}
}
ok了叭莫,dubbo就封裝成http了蹈集。
附件:ResponseUtil的封裝
package com.pingan.testcloud.units;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ui.ModelMap;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
public class ResponseUtil<T> {
private CodeEnum responseEnum;
private T data;
public Logger logger = LoggerFactory.getLogger(this.getClass());
public ResponseUtil(CodeEnum responseEnum, T data){
this.responseEnum=responseEnum;
this.data=data;
}
public ResponseUtil(CodeEnum responseEnum){
this.responseEnum=responseEnum;
}
public ResponseUtil( T data){
this.data=data;
}
@Override
public String toString() {
ModelMap modelMap = new ModelMap();
if (responseEnum != null)
{
modelMap.put("code", responseEnum.getCode());
modelMap.put("message", responseEnum.getMessage());
}
if (data != null) {
modelMap.put("data", data);
}
String jsonString=JSON.toJSONString(modelMap);
logger.info("返回前端的結(jié)果為:"+jsonString);
return jsonString;
}
/**
* @return the responseEnum
*/
public CodeEnum getResponseEnum() {
return responseEnum;
}
/**
* @param responseEnum the responseEnum to set
*/
public void setResponseEnum(CodeEnum responseEnum) {
this.responseEnum = responseEnum;
}
/**
* @return the data
*/
public T getData() {
return data;
}
/**
* @param data the data to set
*/
public void setData(T data) {
this.data = data;
}
public String toJSONString(){
ModelMap modelMap = new ModelMap();
if (responseEnum != null)
{
modelMap.put("code", responseEnum.getCode());
modelMap.put("message", responseEnum.getMessage());
}
if (data != null) {
modelMap.put("data", data);
}
SerializeConfig mapping = new SerializeConfig();
String dateFormat;
dateFormat = "yyyy-MM-dd HH:mm:ss";
mapping.put(Date.class, new SimpleDateFormatSerializer(dateFormat));
String jsonString=JSON.toJSONString(modelMap,mapping);
logger.info("返回前端的結(jié)果為:"+jsonString);
return jsonString;
}
}