dubbo的spi机制分析和实战案例(上)

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: dubbo的spi机制分析和实战案例(上)

java里面提供了一种内置的服务提供和发现机制,可以通过配置让一个程序在运行的时候动态加载该类的具体实现。这样子我们可以在调用某个相应接口的时候,同时达到调用某些具体类的实现功能。


具体的代码案例如下所示:


首先定义一个接口和两个接口的实现类


接口


/**
 * @author idea
 * @date 2019/5/16
 */
public interface PersonAction {
    void say();
}
复制代码


接口实现类


/**
 * @author idea
 * @date 2019/5/16
 */
public class SpiMainTest implements PersonAction {
    @Override
    public void say() {
        System.out.println("this is a SpiMainTest");
    }
}
/**
 * @author idea
 * @date 2019/5/16
 * @Version V1.0
 */
public class SpiSubTest implements PersonAction {
    @Override
    public void say() {
        System.out.println("this is a SpiSubTest");
    }
}
复制代码


然后我们需要在META-INF/services的文件夹底下配置一份文件:


(ps:这里的配置文件命名方式为类所在包名+类名)


网络异常,图片无法展示
|


这份文件里面加入以下的配置信息:


(ps:文件里面输入的内容是表示类所在的地址全称,因为java的spi进行类加载的时候需要知道类所在的路径)


com.sise.dubbo.spi.SpiMainTest
com.sise.dubbo.spi.SpiSubTest
复制代码


接着是编写测试类代码


import java.util.ServiceLoader;
/**
 * @author idea
 * @date 2019/5/16
 */
public class Demo {
    public static void main(String[] args) {
        ServiceLoader<PersonAction> serviceLoader=ServiceLoader.load(PersonAction.class);
        System.out.println("this is java spi");
        serviceLoader.forEach(PersonAction::say);
    }
}
复制代码


当我们执行代码之后,会发现控制台输出了相应的内容:


this is java spi
this is a SpiMainTest
this is a SpiSubTest
复制代码


其实jdk自带的spi功能的实现原理分为了以下几步


1.首先通过java.util.ServiceLoader来加载META-INF/services/文件夹底下的类信息

2.在运行期间需要引用相关类的时候,对加载到内存的类进行搜索和分析,进行实例化调用。


为什么是META-INF/services该文件夹呢?


在ServiceLoader类里面,我们可以通过阅读源码看到它在加载配置的时候会指定默认的加载位置META-INF/services文件夹。ServiceLoader会将该文件底下的配置类信息全部加载存储到内存中,然后在接口进行实例化的时候提供相应的实现类进行对象的实例化功能。这一点和ioc的思想有点类似,通过一个可插拔式的方式来对类的实例化进行控制。


网络异常,图片无法展示
|


网络异常,图片无法展示
|


在了解了java的spi功能之后,我们不妨再来看看dubbo的spi扩展机制。


先用一些实际的案例来进行实战的演练,然后再进行原理性的分析:


基于dubbo的spi实现自定义负载均衡算法


dubbo里面提供了一个可扩展的LoadBalance类专门供开发者们进行扩展:


/**
 * LoadBalance. (SPI, Singleton, ThreadSafe)
 * 
 * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
 * 
 * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
 * @author qian.lei
 * @author william.liangf
 */
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
  /**
   * select one invoker in list.
   * 
   * @param invokers invokers.
   * @param url refer url
   * @param invocation invocation.
   * @return selected invoker.
   */
    @Adaptive("loadbalance")
  <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
复制代码


这个类的头部加入了 @SPI 的注解标识,申明了该类是可以进行自定义拓展的。


在了解了loadBalance之后,我们需要在客户端加入自定义的负载均衡器代码,实现loadBalance接口


import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.LoadBalance;
import java.util.List;
/**
 * @author idea
 * @data 2019/5/18
 */
public class MyLoadBalance implements LoadBalance {
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> list, URL url, Invocation invocation) throws RpcException {
        System.out.println("执行自定义的负载均衡算法");
        for (Invoker<T> tInvoker : list) {
            //可以根据url里面的相关参数做负载均衡计算
            System.out.println("url: "+tInvoker.getUrl());
        }
        //默认只请求第一台服务器
        return list.get(0);
    }
}
复制代码


这是最为基本的一种自定义负载均衡策略(永远只能请求一台机器)这种方式过于简陋,那么我们来对应用场景进行一些拓展吧,假设说现在有个需求,由于某些特定的业务常景所需,要求consumer端在9-18点之间只能请求A机器(或者说更多机器),在18-23点之间请求B机器(或者说更多机器),其余时间可以任意请求,那么这个场景下,dubbo自带的负载均衡策略


ConsistentHashLoadBalance,
RandomLoadBalance,
RoundRobinLoadBalance,
LeastActiveLoadBalance


均不支持,负载均衡该如何实现呢?


这个时候我们只能通过spi机制来自定义一套负载均衡策略进行实现了:


package com.sise.dubbo.config.loadBalanceSpi;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.cluster.LoadBalance;
import java.time.LocalTime;
import java.util.List;
import java.util.Random;
/**
 *
 * @author idea
 * @data 2019/5/18
 */
public class MyLoadBalance implements LoadBalance {
    private final String A_MACHINE_HOST_PORT = "192.168.43.191:20880";
    private final String B_MACHINE_HOST_PORT = "192.168.43.191:20880";
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> list, URL url, Invocation invocation) throws RpcException {
        System.out.println("执行自定义的负载均衡算法");
        //模拟场景
        System.out.println(url);
        int currentHour = LocalTime.now().getHour();
        if (currentHour >= 9 && currentHour <= 18) {
            System.out.println("请求A机器");
            findInvokerInList(list, A_MACHINE_HOST_PORT);
        } else if (currentHour >= 18 && currentHour <= 23) {
            System.out.println("请求B机器");
            findInvokerInList(list, B_MACHINE_HOST_PORT);
        }
        int randIndex = new Random().nextInt(list.size());
        return list.get(randIndex);
    }
    /**
     * 从服务列表里面进行dubbo服务地址匹配
     *
     * @param list
     * @param matchKey
     * @param <T>
     * @return
     */
    private <T> Invoker findInvokerInList(List<Invoker<T>> list, String matchKey) {
        for (Invoker tInvoker : list) {
            String addr = tInvoker.getUrl().getHost() + tInvoker.getUrl().getPort();
            if (matchKey.equals(addr)) {
                return tInvoker;
            }
        }
        return null;
    }
}
复制代码


然后在META-INF/dubbo文件夹底下配置一份纯文本的配置文件,文件命名为:


com.alibaba.dubbo.rpc.cluster.LoadBalance 
复制代码


(ps:不同版本的dubbo,LoadBalance的包名可能不同)


网络异常,图片无法展示
|


在这份文件里面写入这么一行内容(有点key,value的味道)


mylb=com.sise.dubbo.config.loadBalanceSpi.MyLoadBalance
复制代码


在consumer端的配置文件中写入以下内容,这里的loadbalance需要和配置文件里的mylb一致。


<dubbo:reference interface="com.sise.dubbo.api.UserRpcService" id="userRpcService" loadbalance="mylb" />
复制代码


然后我们可以启动多台provider,用consumer去调用这些服务进行测试,通过调整机器的时间点,控制台就会打印出不同的属性信息


请求B机器
执行自定义的负载均衡算法
zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=consumer&check=false&dubbo=2.5.3&interface=com.sise.dubbo.api.UserRpcService&loadbalance=mylb&methods=findByUsername,findAll,printStr&pid=12460&printStr.async=true&service.filter=MyFilter&side=consumer&timestamp=1558143174084&weight=1600
请求A机器
执行自定义的负载均衡算法
zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=consumer&check=false&dubbo=2.5.3&interface=com.sise.dubbo.api.UserRpcService&loadbalance=mylb&methods=findByUsername,findAll,printStr&pid=12460&printStr.async=true&service.filter=MyFilter&side=consumer&timestamp=1558143174084&weight=1600
复制代码


通过上述的这种思路,我们借助dubbo的spi机制来加载满足自己特殊业务的负载均衡器,使得该框架的灵活性更高,扩展性更强。


\

自定义的dubbo过滤器


基于spi的扩展机制,dubbo里面还提供了对于filter类型的自定义拓展。开发者可以自定义一套filter来进行对于请求的功能拦截和校验,这个有点类似于springmvc里面的filter过滤器,通过特定的过滤器拦截数据之后,可以结合特殊的业务场景来做一些控制性的功能。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
24天前
|
负载均衡 监控 Dubbo
Dubbo 原理和机制详解(非常全面)
本文详细解析了 Dubbo 的核心功能、组件、架构设计及调用流程,涵盖远程方法调用、智能容错、负载均衡、服务注册与发现等内容。欢迎留言交流。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
Dubbo 原理和机制详解(非常全面)
|
1月前
|
Dubbo IDE Java
dubbo学习二:下载Dubbo-Admin管理控制台,并分析在2.6.1及2.6.1以后版本的变化
这篇文章是关于如何下载和部署Dubbo管理控制台(dubbo-admin)的教程,并分析了2.6.1版本及以后版本的变化。
45 0
dubbo学习二:下载Dubbo-Admin管理控制台,并分析在2.6.1及2.6.1以后版本的变化
|
2月前
|
Dubbo Java 应用服务中间件
微服务框架Dubbo环境部署实战
微服务框架Dubbo环境部署的实战指南,涵盖了Dubbo的概述、服务部署、以及Dubbo web管理页面的部署,旨在指导读者如何搭建和使用Dubbo框架。
245 17
微服务框架Dubbo环境部署实战
|
2月前
|
缓存 负载均衡 Dubbo
Dubbo技术深度解析及其在Java中的实战应用
Dubbo是一款由阿里巴巴开源的高性能、轻量级的Java分布式服务框架,它致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
81 6
|
3月前
|
负载均衡 Dubbo Java
Dubbo服务Spi机制和原理
该文章主要介绍了Dubbo中的SPI(Service Provider Interface)机制和原理,包括SPI的基本概念、Dubbo中的SPI分类以及SPI机制的实现细节。
Dubbo服务Spi机制和原理
|
3月前
|
Dubbo Java Nacos
【实战攻略】破解Dubbo+Nacos+Spring Boot 3 Native打包后运行异常的终极秘籍——从零开始彻底攻克那些让你头疼不已的技术难题!
【8月更文挑战第15天】Nacos作为微服务注册与配置中心受到欢迎,但使用Dubbo+Nacos+Spring Boot 3进行GraalVM native打包后常遇运行异常。本文剖析此问题及其解决策略:确认GraalVM版本兼容性;配置反射列表以支持必要类和方法;采用静态代理替代动态代理;检查并调整配置文件;禁用不支持的功能;利用日志和GraalVM诊断工具定位问题;根据诊断结果调整GraalVM配置。通过系统排查方法,能有效解决此类问题,确保服务稳定运行。
101 0
|
5月前
|
Dubbo Java 应用服务中间件
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
Spring Boot 调用 Dubbo 接口与编写 Dubbo 接口实战
649 1
|
6月前
|
设计模式 JSON Dubbo
超越接口:探索Dubbo的泛化调用机制
超越接口:探索Dubbo的泛化调用机制
384 0
|
6月前
|
Dubbo Java 应用服务中间件
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用
|
1月前
|
Dubbo Java 应用服务中间件
Spring Cloud Dubbo:微服务通信的高效解决方案
【10月更文挑战第15天】随着信息技术的发展,微服务架构成为企业应用开发的主流。Spring Cloud Dubbo结合了Dubbo的高性能RPC和Spring Cloud的生态系统,提供高效、稳定的微服务通信解决方案。它支持多种通信协议,具备服务注册与发现、负载均衡及容错机制,简化了服务调用的复杂性,使开发者能更专注于业务逻辑的实现。
58 2
下一篇
无影云桌面