(完)SpringCloudAlibaba高并发仿斗鱼直播平台实战

88 阅读6分钟

SpringCloudAlibaba高并发仿斗鱼直播平台实战

xia讠果☛ lexuecode.com/7069.html

常见的微服务架构模式有哪些

  1. 单体应用架构:将所有功能模块集成在一个应用中。
  2. 分层架构:将应用拆分为多个层级,例如展示层、服务层和数据层。
  3. 外部化配置:将配置信息从代码中分离,使得配置可以在不同环境中动态调整。
  4. 服务注册与发现:使用服务注册中心来注册和发现各个微服务实例,以便于服务能动态发现和通信。
  5. 断路器模式:引入断路器来防止故障的蔓延,当一个服务不可用时,断路器会打开并快速失败。
  6. 负载均衡:将流量均匀地分配到多个微服务实例上,以提高系统的性能和可用性。
  7. 容器化部署:使用容器技术(如Docker)来封装和部署微服务,提高部署的灵活性和效率。
  8. 事件驱动架构:通过消息队列实现微服务之间的异步通信,实现解耦和扩展性。
  9. API 网关:提供一个统一的入口点,用于路由请求到相应的微服务实例。
  10. 自动化部署与监控:利用自动化工具来进行部署和监控,确保微服务系统的稳定性和可靠性。

SpringCloudAlibaba高并发仿斗鱼直播平台实战 - Dubbo服务暴露原理

1.远程暴露

远程暴露是指将服务发布到远程注册中心,供消费者调用。Dubbo支持多种远程通信协议,例如dubbo、rmi、hessian、http等。远程暴露的实现原理大致如下:

服务提供者在启动时,将服务实现类通过协议进行网络传输,发布到注册中心上。

服务消费者在启动时,从注册中心上获取到服务提供者的地址,然后通过协议进行网络通信,调用服务提供者的方法。

2.本地暴露

本地暴露是指将服务发布到本地JVM上,供同一JVM中的其他服务调用。Dubbo的本地通信使用了JVM内部的IPC或者共享内存等方式,从而避免了网络传输的开销。本地暴露的实现原理大致如下:

服务提供者在启动时,将服务实现类通过代理方式,注入到本地JVM的缓存中。

服务消费者在启动时,直接从本地JVM缓存中获取到服务提供者的代理对象,然后调用服务提供者的方法。

我们来看进行服务暴露(或者说导出)的基础方法 ServiceConfig 下私有方法 exportUrl

private void exportUrl(URL url, List<URL> registryURLs) {
    String scope = url.getParameter(SCOPE_KEY);
    // 如果配置scope为"none" ,就不进行服务暴露
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
        // 除非显式的配置了远程暴露,不然都会进行本地暴露
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        // 除非显式的指定了本地暴露,不然都会进行远程暴露
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
           // 远程导出省略.....
        }
    }
    // 不难看出,`scope` 字段采取的判断逻辑非常宽容,试想一下,如果填入 scope = "abc" 会发生什么?
    this.urls.add(url);
}

通过上述方法,我们知道了除非指定某接口就是远程暴露,否则都会进行本地暴露。那具体本地暴露是怎么回事呢?我们继续来看

private Protocol protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
String LOCAL_PROTOCOL = "injvm";
String LOCALHOST_VALUE = "127.0.0.1";

private void exportLocal(URL url) {
    URL local = URLBuilder.from(url)
        .setProtocol(LOCAL_PROTOCOL)
        .setHost(LOCALHOST_VALUE)
        .setPort(0)
        .build();
    local = local.setScopeModel(getScopeModel())
        .setServiceModel(providerModel);
    doExportUrl(local, false);
    logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}

private void doExportUrl(URL url, boolean withMetaData) {
    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
    if (withMetaData) {
        invoker = new DelegateProviderMetaDataInvoker(invoker, this);
    }
    Exporter<?> exporter = protocolSPI.export(invoker);
    exporters.add(exporter);
}

不难发现,本地暴露并没有采用取巧的方式去做特殊处理,而是老老实实构建了个URL,并且像模像样的进行导出,只是这个URL的设置了协议类型为injvm,主机更是直接127.0.0.1。当然最重要的是 protocolSPI.export(invoker) 这个代码,这里的 protocolSPI 是利用的Dubbo-SPI的自适应机制,所以导致最终执行导出的实现类为 InjvmProtocol (关于Dubbo-SPI的内容可见 一篇文章让你精通Dubbo的SPI机制),我们来看 InjvmProtocol 的 export 方法

// AbstractProtocol.class
protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<>();

// InjvmProtocol.class 继承自 AbstractProtocol
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

// InjvmExporter.class
public class InjvmExporter<T> extends AbstractExporter<T> {

    private final String key;

    private final Map<String, Exporter<?>> exporterMap;

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);
    }

    @Override
    public void afterUnExport() {
        exporterMap.remove(key);
    }
}   

可以看到这里其实使用了一个 Map 结构存储了这样的服务暴露的结果,因为这个Map 结构存储在抽象类中,也就是说不管是本地暴露还是哪种RPC协议的暴露,实际都会存储在该位置,并没有什么不同。

SpringCloudAlibaba高并发仿斗鱼直播平台实战 - 分库分表原理

分库分表是一种常见的数据库水平拆分策略,主要是为了应对单个数据库的性能瓶颈和数据规模扩展的问题。在分库分表的架构中,数据会被分散存储在多个数据库实例和数据表中,以提高系统的扩展性和性能。

分库指的是将数据库中的数据按照一定规则拆分到不同的数据库中,每个数据库实例负责存储一部分数据。常见的分库策略有按照业务模块、按照数据量来划分数据库。

分表指的是将数据库表按照一定规则拆分到不同的物理表中,每个物理表存储数据的一部分。常见的分表策略有按照数据范围、按照数据哈希值来划分表。

分库分表的原理主要包括以下几个核心概念:

  1. 数据分片:将数据按照某种规则进行分片,确保每个分片中的数据量适中,避免数据倾斜和热点问题。

  2. 路由规则:确定如何将查询请求路由到正确的数据库实例和数据表中,通常借助于分片键来进行路由。

  3. 事务管理:在分库分表的环境下,跨库事务的处理变得更加复杂,需要仔细设计和管理事务边界,避免数据一致性问题。

  4. 容灾恢复:由于数据分散存储在多个地方,需要考虑容灾和数据恢复的方案,以确保系统的可用性和数据的安全性。

  5. 性能优化:通过合理的分库分表策略,可以提高系统的并发能力和查询性能,从而更好地支撑系统的扩展和业务需求。