SpringCloudAlibaba高并发仿斗鱼直播平台实战
xia讠果☛
lexuecode.com/7069.html
常见的微服务架构模式有哪些
- 单体应用架构:将所有功能模块集成在一个应用中。
- 分层架构:将应用拆分为多个层级,例如展示层、服务层和数据层。
- 外部化配置:将配置信息从代码中分离,使得配置可以在不同环境中动态调整。
- 服务注册与发现:使用服务注册中心来注册和发现各个微服务实例,以便于服务能动态发现和通信。
- 断路器模式:引入断路器来防止故障的蔓延,当一个服务不可用时,断路器会打开并快速失败。
- 负载均衡:将流量均匀地分配到多个微服务实例上,以提高系统的性能和可用性。
- 容器化部署:使用容器技术(如Docker)来封装和部署微服务,提高部署的灵活性和效率。
- 事件驱动架构:通过消息队列实现微服务之间的异步通信,实现解耦和扩展性。
- API 网关:提供一个统一的入口点,用于路由请求到相应的微服务实例。
- 自动化部署与监控:利用自动化工具来进行部署和监控,确保微服务系统的稳定性和可靠性。
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高并发仿斗鱼直播平台实战 - 分库分表原理
分库分表是一种常见的数据库水平拆分策略,主要是为了应对单个数据库的性能瓶颈和数据规模扩展的问题。在分库分表的架构中,数据会被分散存储在多个数据库实例和数据表中,以提高系统的扩展性和性能。
分库指的是将数据库中的数据按照一定规则拆分到不同的数据库中,每个数据库实例负责存储一部分数据。常见的分库策略有按照业务模块、按照数据量来划分数据库。
分表指的是将数据库表按照一定规则拆分到不同的物理表中,每个物理表存储数据的一部分。常见的分表策略有按照数据范围、按照数据哈希值来划分表。
分库分表的原理主要包括以下几个核心概念:
-
数据分片:将数据按照某种规则进行分片,确保每个分片中的数据量适中,避免数据倾斜和热点问题。
-
路由规则:确定如何将查询请求路由到正确的数据库实例和数据表中,通常借助于分片键来进行路由。
-
事务管理:在分库分表的环境下,跨库事务的处理变得更加复杂,需要仔细设计和管理事务边界,避免数据一致性问题。
-
容灾恢复:由于数据分散存储在多个地方,需要考虑容灾和数据恢复的方案,以确保系统的可用性和数据的安全性。
-
性能优化:通过合理的分库分表策略,可以提高系统的并发能力和查询性能,从而更好地支撑系统的扩展和业务需求。