dubbo中的本地存根

在使用dubbo调用服务时,有时服务提供方想要服务调用方实现一部分逻辑。例如:添加缓存、参数校验、容错处理等。 […]

在使用dubbo调用服务时,有时服务提供方想要服务调用方实现一部分逻辑。例如:添加缓存、参数校验、容错处理等。其本质是减少调用服务的压力。在dubbo中我们可以使有本地存根功能,来实现上述功能。


因为本地存根是在服务调用方处理的,所以我们只看服务调用方的配置。

consumer.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://code.alibabatech.com/schema/dubbo
       http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 调用方信息-->
    <dubbo:application name="jilinwula"/>

    <!-- 使用zookeeper查找服务地址 -->
    <dubbo:registry address="127.0.0.1:2181" protocol="zookeeper"/>

    <!-- Dubbo自动生成远程服务代理 -->
    <dubbo:reference id="userInfoService" interface="com.jilinwula.api.UserInfoService" stub="com.jilinwula.api.UserInfoServiceStub"/>


</beans>

我们看上述配置中我们指定了stub参数,该参数的作用就是配置我们的本地存根类。其中底层的原理是当我们在服务调用方提供Stub类后,那么当调用方调用服务提供方的dubbo服务时,客户端会生成Proxy实例,这个Proxy实例就是我们正常调用dubbo远程服务要生成的代理实例,然后服务调用方会把Proxy通过构造函数传给调用方中指定的Stub类,这样我们就可以在Stub类中编写相关逻辑来决定要不要去调Proxy实例,也就是要不要发起RPC远程调用,这样也就减轻的服务提供方的服务压力。下面我们看一下UserInfoServiceStub类的源码。

UserInfoServiceStub:

/**
 * Stub服务实现类
 */
public class UserInfoServiceStub implements UserInfoService {

    private final UserInfoService userInfoService;

    public UserInfoServiceStub(UserInfoService userInfoService) {
        this.userInfoService = userInfoService;
    }

    public String get(String username) {
        try {
            if("".equals(username)) {
                return "";
            }
            return String.format("stub:	%s", userInfoService.get(username));
        } catch (Exception e) {
            return "容错处理";
        }
    }
}

我们在Stub类中的实现逻辑比较简单,只是进行了简单的为空判断,如果username为空时,我们直接返回的空字符串,所以此时是不会发起PRC调用的,只有当username不为空时,才会发起PRC调用,并且使用本地存根时,如果在调用服务提供方异常时,我们也可以在Stub类中做出相应的容错处理。下面我们来看一下这3种情况时的日志输出。

Consumer:

/**
 * 服务调用方
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
        context.start();

        UserInfoService userInfoService = context.getBean("userInfoService", UserInfoService.class);

        System.out.println(String.format("userInfoService:%s", userInfoService.get("")));
    }
}

日志:

2018-08-22 17:16:29:832 INFO [ProductBossService,com.alibaba.dubbo.config.ReferenceConfig,425]:  [DUBBO] Refer dubbo service com.jilinwula.api.UserInfoService from url zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=jilinwula&check=false&dubbo=2.6.2&generic=false&interface=com.jilinwula.api.UserInfoService&methods=get&pid=9476:registered:ister.ip=192.168.5.179&remote.timestamp=1534929381419&side=consumer&stub=com.jilinwula.api.UserInfoServiceStub×tamp=1534929388619, dubbo version: 2.6.2, current host: 192.168.5.179
userInfoService:
2018-08-22 17:16:29:845 INFO [ProductBossService,com.alibaba.dubbo.config.AbstractConfig$1,81]:  [DUBBO] Run shutdown hook now., dubbo version: 2.6.2, current host: 192.168.5.179

此时服务提供方是没有任何调用日志的,因为我们并没有发起PRC调用。下面我们修改一个客户端代码,来正确调用服务。

Consumer:

/**
 * 服务调用方
 */
public class Consumer {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"consumer.xml"});
        context.start();

        UserInfoService userInfoService = context.getBean("userInfoService", UserInfoService.class);

        System.out.println(String.format("userInfoService:%s", userInfoService.get("jilinwula")));
    }
}

日志:

2018-08-22 17:20:50:464 INFO [ProductBossService,com.alibaba.dubbo.config.ReferenceConfig,425]:  [DUBBO] Refer dubbo service com.jilinwula.api.UserInfoService from url zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=jilinwula&check=false&dubbo=2.6.2&generic=false&interface=com.jilinwula.api.UserInfoService&methods=get&pid=16480:registered:ister.ip=192.168.5.179&remote.timestamp=1534929381419&side=consumer&stub=com.jilinwula.api.UserInfoServiceStub×tamp=1534929649207, dubbo version: 2.6.2, current host: 192.168.5.179
userInfoService:stub:    jilinwula
2018-08-22 17:20:50:673 INFO [ProductBossService,com.alibaba.dubbo.config.AbstractConfig$1,81]:  [DUBBO] Run shutdown hook now., dubbo version: 2.6.2, current host: 192.168.5.179

此时服务提供方也会输出相关调用日志,因为我们发起了RPC调用。下面我们修改服务提供方代码,让服务提供方抛出异常,来看一下Stub类中是否可能拦截到相关异常。

UserInfoServiceImpl:

/**
 * 服务实现类
 */
@Service
public class UserInfoServiceImpl implements UserInfoService {
    public String get(String username) {
        System.out.println(1 / 0);
        return username;
    }
}

其它代码不需要做任何改动,所以我们直接看日志输出。

日志:

2018-08-22 17:26:10:694 INFO [ProductBossService,com.alibaba.dubbo.config.ReferenceConfig,425]:  [DUBBO] Refer dubbo service com.jilinwula.api.UserInfoService from url zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=jilinwula&check=false&dubbo=2.6.2&generic=false&interface=com.jilinwula.api.UserInfoService&methods=get&pid=21280:registered:ister.ip=192.168.5.179&remote.timestamp=1534929888687&side=consumer&stub=com.jilinwula.api.UserInfoServiceStub×tamp=1534929969519, dubbo version: 2.6.2, current host: 192.168.5.179
userInfoService:容错处理
2018-08-22 17:26:10:905 INFO [ProductBossService,com.alibaba.dubbo.config.AbstractConfig$1,81]:  [DUBBO] Run shutdown hook now., dubbo version: 2.6.2, current host: 192.168.5.179

这时我们的Stub类成功拦截到了异常。


这就是dubbo本地存根功能,因为我们只是演示该功能的使用,所以上述代码比较简单,在实际的开发中,我们可以根据业务将相关数据缓存,并且在Stub类进行缓存的处理,只有缓存查不到数据时,才发起PRC调用,这样我们就相当于减少了服务调用的次数,从而间接的提升了服务的性能。

项目源码

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧