Dubbo是阿里開源的一個高效能的RPC服務框架,我平時接觸Dubbo介面極多,並且也會為不同業務需求開發Dubbo介面。專案有用到Dubbo作為RPC呼叫的同學可以看看我這篇文章,做到簡單高效的呼叫測試任意Dubbo介面。
最開始我測試Dubbo介面的時候是啟動整個專案後透過postman等呼叫Controller介面來間接測試Dubbo介面,或者使用別人提供的Dubbo介面時,由於有時候對介面返回的資料結構不夠清楚,因此也得透過像前面那樣啟動專案來間接呼叫檢視返回結果,這樣及其浪費時間,且效率低下。
後面我開始寫單元測試來測Dubbo介面,這樣效率稍微高些,但是也有個弊端,就是每次修改請求引數時都得重新執行單元測試,由於現在專案一般都不會很小,所有每次執行單元測試時也挺消耗時間的,大專案的話簡直考驗人的耐心。為了解決這些問題,我深入研究了Dubbo,並找了不少資料,終於讓我找到一個簡便的方法來測試。
只需要兩個類:
測試類DubboTest:
/**
* @author yudong
* @date 2019/6/13
*/
public
class
DubboTest
{
@AfterClass
public
static
void
exit
()
{
System
。
exit
(
0
);
}
@Test
public
void
test
()
{
// UserDubboService是已經註冊到zookeeper註冊中心的Dubbo服務,這裡不指定呼叫特定服務例項
UserDubboService
service
=
DubboUtils
。
getService
(
UserDubboService
。
class
,
“1。0。0”
);
UserBaseDto
userBaseDto
=
service
。
getByUsername
(
“1050106266”
);
System
。
out
。
println
(
userBaseDto
);
// 如果是呼叫測試本地Dubbo介面,只需要把專案啟動起來,然後就可以在這裡隨意修改請求引數測試,非常方便
// 這裡指定呼叫此127。0。0。1地址提供的Dubbo服務
service
=
DubboUtils
。
getService
(
UserDubboService
。
class
,
“1。0。0”
,
“127。0。0。1:21899”
);
userBaseDto
=
service
。
getByUsername
(
“1050106266”
);
System
。
out
。
println
(
userBaseDto
);
}
}
其中DubboUtils類的定義:
/**
* @author yudong
* @date 2019/6/13
*/
public
class
DubboUtils
{
/**
* dubbo服務的zookeeper註冊中心地址
*/
private
static
final
String
ZOOKEEPER_ADDRESS
=
“zookeeper://127。0。0。1:2181”
;
public
static
<
T
>
T
getService
(
Class
<
T
>
dubboServiceClass
,
String
version
)
{
return
getService
(
dubboServiceClass
,
version
,
null
);
}
@SuppressWarnings
(
“ALL”
)
public
static
<
T
>
T
getService
(
Class
<
T
>
dubboServiceClass
,
String
version
,
String
host
)
{
Enhancer
enhancer
=
new
Enhancer
();
enhancer
。
setSuperclass
(
dubboServiceClass
);
enhancer
。
setCallback
((
MethodInterceptor
)
(
obj
,
method
,
args
,
proxy
)
->
{
ReferenceConfig
<
GenericService
>
reference
=
getReferenceConfig
(
dubboServiceClass
,
version
,
host
);
GenericService
genericService
=
reference
。
get
();
return
genericService
。
$invoke
(
method
。
getName
(),
getMethodParamType
(
method
),
args
);
});
Object
service
=
enhancer
。
create
();
return
(
T
)
service
;
}
private
static
<
T
>
ReferenceConfig
<
T
>
getReferenceConfig
(
Class
<?>
interfaceClass
,
String
version
,
String
host
)
{
String
referenceKey
=
interfaceClass
。
getName
();
ReferenceConfig
<
T
>
referenceConfig
=
new
ReferenceConfig
<>();
referenceConfig
。
setApplication
(
getApplicationConfig
());
referenceConfig
。
setRegistry
(
getRegistryConfig
());
referenceConfig
。
setInterface
(
interfaceClass
);
referenceConfig
。
setVersion
(
version
);
referenceConfig
。
setTimeout
(
8000
);
referenceConfig
。
setGeneric
(
true
);
if
(
StringUtils
。
isNotBlank
(
host
))
{
String
url
=
String
。
format
(
“dubbo://%s/%s”
,
host
,
referenceKey
);
referenceConfig
。
setUrl
(
url
);
}
return
referenceConfig
;
}
private
static
RegistryConfig
getRegistryConfig
()
{
RegistryConfig
registryConfig
=
new
RegistryConfig
();
registryConfig
。
setAddress
(
DubboUtils
。
ZOOKEEPER_ADDRESS
);
return
registryConfig
;
}
private
static
ApplicationConfig
getApplicationConfig
()
{
ApplicationConfig
application
=
new
ApplicationConfig
();
application
。
setName
(
“b2c-test”
);
return
application
;
}
private
static
String
[]
getMethodParamType
(
Method
method
)
{
Class
<?>[]
paramClassList
=
method
。
getParameterTypes
();
String
[]
paramTypeList
=
new
String
[
paramClassList
。
length
];
for
(
int
i
=
0
;
i
<
paramClassList
。
length
;
i
++)
{
paramTypeList
[
i
]
=
paramClassList
[
i
]。
getTypeName
();
}
return
paramTypeList
;
}
}
這樣子就能夠方便呼叫任意Dubbo介面啦
下面我分析下是怎麼實現的,核心方法是這個:
@SuppressWarnings
(
“ALL”
)
public
static
<
T
>
T
getService
(
Class
<
T
>
dubboServiceClass
,
String
group
,
String
version
,
String
host
)
{
Enhancer
enhancer
=
new
Enhancer
();
enhancer
。
setSuperclass
(
dubboServiceClass
);
enhancer
。
setCallback
((
MethodInterceptor
)
(
obj
,
method
,
args
,
proxy
)
->
{
ReferenceConfig
<
GenericService
>
reference
=
getReferenceConfig
(
dubboServiceClass
,
group
,
version
,
host
);
GenericService
genericService
=
reference
。
get
();
Object
result
=
genericService
。
$invoke
(
method
。
getName
(),
getMethodParamType
(
method
),
args
);
String
resJsonStr
=
OMUtils
。
objectMapper
()。
writeValueAsString
(
result
);
return
OMUtils
。
objectMapper
()。
readValue
(
resJsonStr
,
method
。
getReturnType
());
});
Object
service
=
enhancer
。
create
();
return
(
T
)
service
;
}
GenericService
是dubbo官方提供的通用服務介面,裡面有個方法:
$invoke(String method, String[] parameterTypes, Object[] args)可用來做泛化呼叫。這個方法比較難用,所以我使用cglib(Enhancer)建立了一個代理來簡化這個泛化呼叫操作,就像我們正常呼叫dubbo API那樣的寫法,詳細用法請看DubboTest類。
原始碼github地址和gitee地址(程式碼同步):