欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

dubbo泛化調(diào)用使用及原理示例解析

 更新時間:2023年09月11日 15:18:14   作者:土豆肉絲蓋澆飯  
這篇文章主要為大家介紹了dubbo泛化調(diào)用使用及原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是泛化調(diào)用

本文案例代碼見 git@github.com:shengchaojie/dubbo_best_practise.git

通常我們想調(diào)用別人的dubbo服務(wù)時,我們需要在項目中引入對應(yīng)的jar包。而泛化調(diào)用的作用是,我們無需依賴相關(guān)jar包,也能調(diào)用到該服務(wù)。

這個特性一般使用在網(wǎng)關(guān)類項目中,在業(yè)務(wù)開發(fā)中基本不會使用。

使用方式

假設(shè)我現(xiàn)在要調(diào)用下面的接口服務(wù)

package com.scj.demo.dubbo.provider.service.impl;
public interface ByeService {
    String bye(String name);
}

api

ReferenceConfig<GenericService> referenceConfig = new ReferenceConfig<>();
referenceConfig.setApplication(new ApplicationConfig("test"));
referenceConfig.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
referenceConfig.setInterface("com.scj.demo.dubbo.provider.service.impl.ByeService");
referenceConfig.setGeneric(true);
GenericService genericService = referenceConfig.get();
Object result = genericService.$invoke(
    "bye",
    new String[]{"java.lang.String"},
    new Object[]{"1234"});
System.out.println(result);

spring

在xml文件做以下配置

<dubbo:reference id="byeService" interface="com.scj.demo.dubbo.provider.service.impl.ByeService" generic="true" />

然后注入使用

@Service
public class PersonService {
    @Resource(name = "byeService")
    private GenericService genericService;
    public void sayBye(){
        Object result = genericService.$invoke(
                "bye",
                new String[]{"java.lang.String"},
                new Object[]{"1234"});
        System.out.println(result);
    }
}

在兩種調(diào)用方式中,我們都需要使用被調(diào)用接口的字符串參數(shù)生成GenericService,通過GenericService的$invoke間接調(diào)用目標(biāo)接口的接口。

public interface GenericService {
    /**
     * Generic invocation
     *
     * @param method         Method name, e.g. findPerson. If there are overridden methods, parameter info is
     *                       required, e.g. findPerson(java.lang.String)
     * @param parameterTypes Parameter types
     * @param args           Arguments
     * @return invocation return value
     * @throws Throwable potential exception thrown from the invocation
     */
    Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;
}

$invoke的三個參數(shù)分別為,方法名,方法參數(shù)類型數(shù)組,方法參數(shù)數(shù)組。

方法入?yún)?gòu)造

可以看到泛化調(diào)用的一個復(fù)雜性在于$invoke的第三個參數(shù)的組裝,下面介紹幾種復(fù)雜入?yún)⒌恼{(diào)用方式

首先豐富提供者接口

public interface ByeService {
    String bye(String name);
    String bye(String name, Long age, Date date);
    String bye(Person person);
    String bye(List<String> names);
    String bye(String[] names);
    String byePersons(List<Person> persons);
    String byePersons(Person[] persons);
    @Data
    public static class Person{
        private String name;
        private Long age;
        private Date birth;
    }
    public static void main(String[] args) {
        System.out.println(Person.class.getName());
    }
}

多參數(shù)

    @Test
    public void testMultiParam(){
        result = genericService.$invoke(
                "bye",
                new String[]{"java.lang.String","java.lang.Long","java.util.Date"},
                new Object[]{"scj",12L,new Date()});
        System.out.println(result);
    }

POJO

    Map<String,Object> personMap = new HashMap<>();
    {
        personMap.put("name","scj");
        personMap.put("age","12");
        personMap.put("birth",new Date());
    }
    @Test
    public void testPOJO(){
        result = genericService.$invoke(
                "bye",
                new String[]{"com.scj.demo.dubbo.provider.service.impl.ByeService$Person"},
                new Object[]{personMap});
        System.out.println(result);
    }

Map

    @Test
    public void testMap(){
        result = genericService.$invoke(
                "bye",
                new String[]{"java.util.Map"},
                new Object[]{personMap});
        System.out.println(result);
    }

集合

    @Test
    public void testList(){
        List<String> names = Lists.newArrayList("scj1","scj2");
        result = genericService.$invoke(
                "bye",
                new String[]{"java.util.List"},
                new Object[]{names});
        System.out.println(result);
    }

數(shù)組

    @Test
    public void testArray(){
        String[] nameArray = new String[]{"scj1","scj3"};
        result = genericService.$invoke(
                "bye",
                new String[]{"java.lang.String[]"},
                new Object[]{nameArray});
        System.out.println(result);
    }

集合+POJO

    @Test
    public void testPOJOList(){
        result = genericService.$invoke(
                "byePersons",
                new String[]{"java.util.List"},
                new Object[]{Lists.newArrayList(personMap,personMap)});

        System.out.println(result);
    }

數(shù)組+POJO

    @Test
    public void testPOJOArray(){
        result = genericService.$invoke(
                "byePersons",
                new String[]{"com.scj.demo.dubbo.provider.service.impl.ByeService$Person[]"},
                new Object[]{Lists.newArrayList(personMap,personMap)});

        System.out.println(result);
    }

結(jié)果返回

與入?yún)⑾嗨?,雖然$invoke的返回定義為Object,實際上針對不同類型有不同的返回。

別想著轉(zhuǎn)換為POJO,你都泛化調(diào)用了,搞不到接口,如何轉(zhuǎn)換。當(dāng)然自己定義一個完全一樣的當(dāng)然也行。

接口返回類型$invoke返回類型
基礎(chǔ)類型基礎(chǔ)類型
POJOHashMap
CollectionList返回ArrayList,Set返回HashSet
ArrayArray
組合類型根據(jù)上述映射組合返回

原理介紹

消費者端

泛化調(diào)用和直接調(diào)用在消費者者端,在使用上的區(qū)別是,我們調(diào)用服務(wù)時使用的接口為GenericService,方法為$invoker。在底層的區(qū)別是,消費者端發(fā)出的rpc報文發(fā)生了變化。

使用方式上的改變

在使用上,不管哪種配置方式,我們都需要配置generic=true

設(shè)置generic=true后,RefereceConfig的interfaceClass會被強(qiáng)制設(shè)置為GenericService

if (ProtocolUtils.isGeneric(getGeneric())) {
    //如果是泛化調(diào)用
    interfaceClass = GenericService.class;
} else {
    try {
        interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                                       .getContextClassLoader());
    } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e.getMessage(), e);
    }
    checkInterfaceAndMethods(interfaceClass, methods);
}

這也使得我們的RefereanceBean返回的是GenericService類型的代理。

invoker = refprotocol.refer(interfaceClass, urls.get(0));

生成的代理是GenericService的代理只是我們使用方式上的變化,更為核心的是,底層發(fā)送的rpc報文發(fā)生了什么變化。

底層報文變化

Dubbo的rpc報文分為header和body兩部分。我們這邊只需要關(guān)注body部分。構(gòu)造邏輯如下

    @Override
    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
        RpcInvocation inv = (RpcInvocation) data;
        out.writeUTF(version);//dubbo版本號
        out.writeUTF(inv.getAttachment(Constants.PATH_KEY));//path 就是接口全限定名
        out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));// 接口版本號
        out.writeUTF(inv.getMethodName());//方法名
        out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));//方法參數(shù)類型
        Object[] args = inv.getArguments();
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                out.writeObject(encodeInvocationArgument(channel, inv, i));//方法參數(shù)
            }
        }
        out.writeObject(RpcUtils.getNecessaryAttachments(inv));//rpc上下文
    }

那么我們通過直接調(diào)用與泛化調(diào)用ByeService的bye方法在報文上有啥區(qū)別呢?

我一開始以為報文中的path是GenericeService,其實并沒有,path就是我們調(diào)用的目標(biāo)方法。

path來源???todo

而報文中的方法名,方法參數(shù)類型以及具體參數(shù),還是按照GenericeService的$invoke方法入?yún)鬟f的。

這么個二合一的報文,發(fā)送到提供者那邊,它估計也會很懵逼,我應(yīng)該怎么執(zhí)行?

所以針對泛化調(diào)用報文還會把generic=true放在attchment中傳遞過去

具體邏輯在GenericImplFilter中。

GenericImplFilter中有很多其他邏輯,比如泛化調(diào)用使用的序列化協(xié)議,正常接口走泛化調(diào)用的模式,我們只需要設(shè)置attachment的那部分。

針對泛化調(diào)用,要進(jìn)行2次序列化/反序列化??聪翽OJO的調(diào)用方式你就知道為啥了

((RpcInvocation) invocation).setAttachment(
    Constants.GENERIC_KEY, invoker.getUrl().getParameter(Constants.GENERIC_KEY));

知道消費者端報文發(fā)生了什么變化,那么接下來就去看提供者端如何處理這個改造后的報文。

interfaceClass和interfaceName的區(qū)別

總結(jié)一下ReferenceConfig中interfaceClass和interfaceName的區(qū)別?(這道面試題好像不錯)

interfaceClass用于指定生成代理的接口
interfaceName用于指定發(fā)送rpc報文中的path(告訴服務(wù)端我要調(diào)用那個服務(wù))

提供者端

消費者泛化調(diào)用的rpc報文傳遞到提供者還不能直接使用,雖然path是對的,但是實際的方法名,參數(shù)類型,參數(shù)要從rpc報文的參數(shù)中提取出來。

GenericFilter就是用來做這件事情。

在提供者這邊,針對泛化調(diào)用的邏輯全部封裝到了GenericFilter,解耦的非常好。

GenericFilter邏輯分析

1. 是否是泛化調(diào)用判斷

if (inv.getMethodName().equals(Constants.$INVOKE)
                &amp;&amp; inv.getArguments() != null
                &amp;&amp; inv.getArguments().length == 3
                &amp;&amp; !GenericService.class.isAssignableFrom(invoker.getInterface())){
    //...
}

注意第4個條件,一開始很疑惑,后來發(fā)現(xiàn)rpc報文中的path是目標(biāo)接口的,這邊invoker.getInterface()返回的肯定就是實際接口了

2. 方法參數(shù)提取

//從argument提取目標(biāo)方法名 方法類型 方法參數(shù)
String name = ((String) inv.getArguments()[0]).trim();
String[] types = (String[]) inv.getArguments()[1];
Object[] args = (Object[]) inv.getArguments()[2];

3. 方法參數(shù)解析,進(jìn)一步反序列化

//反射獲取目標(biāo)執(zhí)行方法
Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
Class<?>[] params = method.getParameterTypes();
if (args == null) {
    args = new Object[params.length];
}
String generic = inv.getAttachment(Constants.GENERIC_KEY);
if (StringUtils.isBlank(generic)) {
    generic = RpcContext.getContext().getAttachment(Constants.GENERIC_KEY);
}
//一些反序列化
if (StringUtils.isEmpty(generic)
    || ProtocolUtils.isDefaultGenericSerialization(generic)) {
    args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
} else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
    for (int i = 0; i < args.length; i++) {
        if (byte[].class == args[i].getClass()) {
            try {
                UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i]);
                args[i] = ExtensionLoader.getExtensionLoader(Serialization.class)
                    .getExtension(Constants.GENERIC_SERIALIZATION_NATIVE_JAVA)
                    .deserialize(null, is).readObject();
            } catch (Exception e) {
                throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e);
            }
        } else {
            throw new RpcException(
                "Generic serialization [" +
                Constants.GENERIC_SERIALIZATION_NATIVE_JAVA +
                "] only support message type " +
                byte[].class +
                " and your message type is " +
                args[i].getClass());
        }
    }
} else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
    for (int i = 0; i < args.length; i++) {
        if (args[i] instanceof JavaBeanDescriptor) {
            args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]);
        } else {
            throw new RpcException(
                "Generic serialization [" +
                Constants.GENERIC_SERIALIZATION_BEAN +
                "] only support message type " +
                JavaBeanDescriptor.class.getName() +
                " and your message type is " +
                args[i].getClass().getName());
        }
    }
}

這邊有個疑問,為什么這邊還要再次反序列化一次,netty不是有decoder么??

嗯,你別忘了,針對一個POJO你傳過來是一個Map,從Map轉(zhuǎn)換為POJO需要這邊進(jìn)一步處理。

4. 調(diào)用目標(biāo)服務(wù)

Result result = invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));

這邊的invoker就是實際服務(wù)提供者的invoker,因為我們的path是正確的,invoker獲取在DubboProtocl的requestHandler回調(diào)中

5. 異常處理

if (result.hasException()
    &amp;&amp; !(result.getException() instanceof GenericException)) {
    return new RpcResult(new GenericException(result.getException()));
}

這邊需要注意一下??!針對接口的泛化調(diào)用,拋出的異常都會經(jīng)過GenericException包裝一下。

總結(jié)

從功能上來看,泛化調(diào)用提供了在沒有接口依賴情況下進(jìn)行的解決方案,豐富框架的使用場景。
從設(shè)計上來看,泛化調(diào)用的功能還是通過擴(kuò)展的方式實現(xiàn)的,侵入性不強(qiáng),值得學(xué)習(xí)借鑒。

以上就是dubbo泛化調(diào)用使用及原理示例解析的詳細(xì)內(nèi)容,更多關(guān)于dubbo泛化調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中mybatis關(guān)于example類的使用詳解

    Java中mybatis關(guān)于example類的使用詳解

    這篇文章主要介紹了Java中mybatis中關(guān)于example類的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 淺談一下Java中的訪問修飾符以及作用

    淺談一下Java中的訪問修飾符以及作用

    這篇文章主要介紹了淺談一下Java中的訪問修飾符以及作用,修飾符修飾的是“被訪問”的權(quán)限,所有修飾符都可以修飾成員變量,方法,構(gòu)造方法,需要的朋友可以參考下
    2023-05-05
  • 使用Spring方法攔截器MethodInterceptor

    使用Spring方法攔截器MethodInterceptor

    這篇文章主要介紹了使用Spring方法攔截器MethodInterceptor,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java中Equals使用方法匯總

    Java中Equals使用方法匯總

    這篇文章主要采用問答的方式集中講解了Java中Equals的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Java中UUID生成原理及優(yōu)缺點

    Java中UUID生成原理及優(yōu)缺點

    本文將詳細(xì)講解UUID的生成原理、特性、實用場景以及優(yōu)缺點,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Java編程實現(xiàn)五子棋人人對戰(zhàn)代碼示例

    Java編程實現(xiàn)五子棋人人對戰(zhàn)代碼示例

    這篇文章主要介紹了Java編程實現(xiàn)五子棋人人對戰(zhàn)代碼示例,具有一定借鑒價值,需要的朋友可以參考下。
    2017-11-11
  • 在?Java?中將Object?轉(zhuǎn)換為?Int的四種方法

    在?Java?中將Object?轉(zhuǎn)換為?Int的四種方法

    這篇文章主要介紹了在Java中如何將?Object?轉(zhuǎn)換為Int,本文研究了在?Java中將Object轉(zhuǎn)換為int的四種不同方法,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • 詳解SpringMVC的類型轉(zhuǎn)換及驗證方法

    詳解SpringMVC的類型轉(zhuǎn)換及驗證方法

    在本篇文章里面我們給大家詳細(xì)分析了SpringMVC的類型轉(zhuǎn)換及驗證方法的相關(guān)知識,對此有需要的朋友們學(xué)習(xí)下吧。
    2018-10-10
  • java返回集合為null還是空集合及空集合的三種寫法小結(jié)

    java返回集合為null還是空集合及空集合的三種寫法小結(jié)

    這篇文章主要介紹了java返回集合為null還是空集合及空集合的三種寫法小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java讀取zip/jar包中文件的幾種方式

    java讀取zip/jar包中文件的幾種方式

    這篇文章主要給大家介紹了關(guān)于java讀取zip/jar包中文件的幾種方式,在我們?nèi)粘J褂弥袎嚎s文件是非常常用的,文中通過示例代碼將java讀取zip/jar包中文件的方法介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07

最新評論