跳到主要内容

分布式改造

在构建 API 开放平台的过程中,随着业务的发展和用户需求的增加,我们面临着一些挑战,其中之一就是如何实现高效的分布式架构,以满足系统的可扩展性和稳定性要求。

问题与背景

在网关项目的开发中,我们遇到了一些问题。网关项目本身比较纯净,没有操作数据库的包,但它需要调用之前写过的代码来实现一些关键功能,如用户鉴权、接口查询和调用统计等。如果采用复制粘贴的方式来维护这些代码,将会带来很多麻烦,不仅增加了开发成本,还容易导致代码的不一致性和维护困难。

解决方案

为了解决这个问题,有几种调用其他项目方法的方式:

  1. 复制代码、环境和依赖:这种方式虽然简单直接,但存在明显的缺点。它会导致代码的冗余和重复,增加了维护成本,而且在不同项目中同步更新代码也会变得非常困难。
  2. HTTP 请求:通过提供一个接口供其他项目调用,这种方式在一定程度上可以解决问题,但也存在一些不足。例如,它需要额外的网络开销,可能会影响性能,而且对于调用方来说,需要处理更多的网络相关的逻辑,增加了开发的复杂性。
  3. RPC:RPC(Remote Procedure Call,远程过程调用)是一种更优的选择。它的作用是像调用本地方法一样调用远程方法,对开发者来说更加透明,减少了很多沟通成本。此外,RPC 向远程服务器发送请求时,未必要使用 HTTP 协议,可以根据实际需求选择更合适的通信协议,提高了性能和灵活性。
  4. 把公共代码打 jar 包,其他项目引用,客户端 SDK:这种方式可以实现代码的复用,但需要确保 jar 包的版本管理和兼容性,同时对于客户端来说,需要引入额外的 SDK,增加了开发的复杂性。

综合考虑,我们选择了 RPC 作为主要的解决方案,并采用 Dubbo 框架来实现。

Dubbo框架

官网:https://cn.dubbo.apache.org/zh-cn/overview/quickstart/

Dubbo 框架是一个高性能、轻量级的开源 RPC 框架,它提供了两种使用方式:

  1. Spring Boot 代码(注解 + 编程式):通过编写 Java 接口,服务提供者和消费者都可以引用这个接口。这种方式简单易用,通过注解和编程式的配置,可以方便地实现服务的注册和调用。
  2. IDL(接口调用语言**)**:创建一个公共的接口定义文件,服务提供者和消费者读取这个文件。这种方式的优点是跨语言,所有的框架都可以认识这个定义文件,从而实现不同语言之间的服务调用。

在项目中,将 backend 项目作为服务提供者,提供了三个关键方法:

  1. 实际情况应该是去数据库中查是否已分配给用户:通过这个方法,网关可以验证用户的身份和权限,确保只有合法的用户才能调用接口。
  2. 从数据库中查询模拟接口是否存在,以及请求方法是否匹配(还可以校验请求参数):这个方法用于验证接口的有效性和请求的合法性,保证系统的安全性和稳定性。
  3. 调用成功,接口调用次数 + 1 invokeCount:用于统计接口的调用次数,为后续的分析和计费提供数据支持。

而 gateway 项目作为服务调用者,通过@DubboReference注解来调用这三个方法,实现了与 backend 项目的无缝集成。此外,我们使用 nacos 作为注册中心,来管理服务的注册和发现。这样,当服务提供者的地址发生变化时,服务调用者可以自动发现并切换到新的服务地址,提高了系统的可靠性和可扩展性。

具体实现

yunfei-api-back项目中引入dubbo:

    <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.0.9</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.1.0</version>
</dependency>

配置:

dubbo:  
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
id: nacos-registry
address: nacos://localhost:8848

yunfei-api-gateway模块下面:

    <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>3.0.9</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>2.1.0</version>
</dependency>

配置:

dubbo:  
application:
name: dubbo-springboot-demo-provider
protocol:
name: dubbo
port: -1
registry:
id: nacos-registry
address: nacos://localhost:8848

主类开启,每个要远程调用的主类都要:

@EnableDubbo  
public class MyApplication

在公共模块中编写接口yunfei-api-common

public interface InnerInterfaceInfoService {  
/**
* 从数据库中查询模拟接口是否存在(请求路径、请求方法、请求参数)
*/
InterfaceInfo getInterfaceInfo(String path, String method);
}
public interface InnerUserInterfaceInfoService {

/**
* 调用接口统计
* @param interfaceInfoId
* @param userId
* @return
*/
boolean invokeCount(long interfaceInfoId, long userId);
}
public interface InnerUserService {

/**
* 数据库中查是否已分配给用户秘钥(accessKey)
* @param accessKey
* @return
*/
User getInvokeUser(String accessKey);
}

yunfei-api-back项目中实现这些接口的具体内容。

@DubboService  
public class InnerInterfaceInfoServiceImpl implements InnerInterfaceInfoService

在网关项目中就可以进行调用了:

@DubboReference  
private InnerUserService innerUserService;