穿越微服务迷雾:揭秘一次微服务设计实战

前言

最近学校课程要求做一个简单的微服务系统,正好借此机会拆解一下微服务系统的架构设计。

系统要求如下:

该基础业务平台应包含以下功能:

  1. 基于RBAC的系统权限管理功能,支持用户、角色和权限的可配置功能。在此基础上,实现基本的用户登录、注册等用户管理方面的功能。
  2. 提供统一的系统安全、缓存、日志、异常处理等功能接口。
  3. 提供统一的后台管理功能,可以对系统中的数据表进行后台管理。
  4. 在基础框架上,设计一个简单的业务场景(应至少涉及两个不同的角色、两个不同的业务部门),实现基本的业务功能。

平台的非功能方面的要求:

  1. 采用微服务架构:提供统一的服务网关、服务注册发现和服务间通信接口以及其他的服务管理方案。
  2. 采用前后端完全分离的架构。
  3. 兼容性要求:支持主流的浏览器,包括Chrome、Edge、Firefox和Safari。
  4. 安全性要求:系统达到等级保护二级要求,能够通过相应的安全性测试。
  5. 性能要求:系统应支持100并发,给出相应的测试结果。

单体架构

我们都知道单体项目的主流架构是分层架构,大概分为controller、service、dao、entity等层级,每个板块下可能会有多个业务类。但是单体项目中一个项目只有一个模块,所有的"包"都直接放在这个大模块中即可。

  • Controller层:
    • 作用: 负责接收用户的请求,调用相应的业务逻辑处理,并返回结果给用户。
    • 实现: 包含处理HTTP请求的逻辑,解析请求参数,调用Service层的业务逻辑,最后将结果返回给用户。
  • Service层:
    • 作用: 执行业务逻辑,处理业务规则和流程,对数据进行处理和组织。
    • 实现: 包含具体的业务逻辑,调用Dao层进行数据的读写,确保业务的正确执行。
  • Dao(Data Access Object)层:
    • 作用: 负责与数据库进行交互,执行数据的持久化操作。
    • 实现: 包含数据库操作的具体实现,例如SQL语句的增删改查等。
  • Entity层:
    • 作用: 表示业务领域中的实体,通常与数据库中的表结构相关。
    • 实现: 包含数据的定义,通常是一个与数据库表结构对应的Java类,类中的属性对应表中的字段。

首先我们来看看该项目的单体架构图,每层中的user、auth、business1、business2都是业务类

分模块+分层架构

然而该项目要求用微服务架构,微服务项目中通常需要按业务拆分出多个模块,那么项目架构是怎样的呢?

所以首先我们需要将该项目按业务分模块,那么就变为了分模块+分层架构。在微服务中还有两个要求:

  • gateway作为服务网关统一管理请求
  • 每个服务需要能调用其他服务模块,所以每个服务模块中还需要加上client这一层
  • 每个服务需要能提供服务模块给其他模块调用,所以每个服务模块中还需要加上api这一层

api层、client层、controller层各代表什么意思?

  • **api层:**提供给内部服务调用的一系列接口,属于被调用者
  • **client层:**主要负责发送HTTP请求需要调用的模块,可以理解为一个发送HTTP请求的封装层,属于调用者
  • **controller层:**提供给外部服务调用的一系 列接口,属于被调用者

api层与controller层的异同

  • **相同点:**都是提供HTTP接口给外部服务调用
  • **不同点:**controller层提供给外部服务(例如前端调用),而api层提供给内部服务调用,比如架构图中的其他服务

关系还是有点乱的,直接一图解千愁吧!

那么据此得来的架构图如下

这样是否就可行了呢?能用但问题大了,强烈不推荐:

极度高耦合:(1)A、B服务调用C服务时都需要写相同的client层(假设叫Cclient类),那么Cclient类需要复制两份分别在A、B服务中。(2)且Cclient类的方法返回值一定是C服务中的实体类(例如C服务是User服务那么可能是UserDO的数据库实体类),那么还需要将涉及到的所有实体类一并复制两份给A、B服务。

好家伙,一旦Capi相关的实体类有任何改动,那么Capi需要更改,调用C服务的Cclient代码中相关的实体类自然也全都需要修改,简直是灾难啊!

如何解决?

  1. 首先解决Cclient类需要复制两份的问题

    一个经典解法就是抽取模块,功能复用。将client层的代码给抽取出来写一个统一的HTTP请求服务工具,那么client层的代码就只需要引入工具调用即可,实现功能复用。不过还好,这个问题@FeignClient注解帮我们完成了,详情看这篇文章[《Java远程调用神器:@FeignClient揭秘,轻松搞定微服务通信!》](Java远程调用神器:@FeignClient揭秘,轻松搞定微服务通信! - 掘金 (juejin.cn))

  2. 然后解决实体类需要复制两份的问题

    其实从这篇文章[《Java远程调用神器:@FeignClient揭秘,轻松搞定微服务通信!》](Java远程调用神器:@FeignClient揭秘,轻松搞定微服务通信! - 掘金 (juejin.cn))中已经可以看出解决方法了,聪明如你,应该发现了client和api两个类几乎代码都一样。所以我们就可以想方法复用,将client继承api,然后client加上了@FeignClient注解即可,这就引出了微服务架构了

微服务架构

经过上面的分析,我们将上面提到的api以及相应的实体类抽取出来形成单独的模块,作为interface模块,那么另一个对应的就是service模块,即每一个业务对应的服务模块再分成interface和service模块,架构图及依赖关系如下

依赖关系使用Maven即可实现

xml 复制代码
	<!-- cloud-user-service的pom依赖 -->
    <dependencies>
        <dependency>
            <groupId>com.cloud.auth</groupId>
            <artifactId>cloud-auth-interface</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        ......
        <dependency>
            <groupId>com.cloud.common</groupId>
            <artifactId>cloud-common-web</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

结束了吗?到这里系统基本可用,没有大问题,但还是不太推荐,因为实体类全部放到interface模块中不太优雅。

在《阿里巴巴Java开发手册》中写到,实体类还分为DO、DTO、VO等等类型。

  • **DO:**与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。

  • **DTO:**数据传输对象,Service 或 Manager 向外传输的对象。

  • **VO:**显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

《阿里巴巴Java开发手册》可在公众号免费获取资源

一般常用到上面三种领域模型,这三种领域模型全放到interface模块中合适吗?

答案显然是否定的,DO类是与数据库一一映射的,只有dao层才会请求数据库返回DO类对象。而interface模块的目的是暴露出api给其他服务调用,那么api层更应该返回一个DTO对象而不会返回DO对象。那么也就是说interface模块一定不会需要用到DO类,于是我们得出结论:DO类应该在service模块中,而除了DO类的其他领域对象留在interface模块中,于是我们终于得到最终的架构图了!

如果您觉得该文章有用,欢迎点赞、留言并分享给更多人。感谢您的支持!

相关推荐
弥琉撒到我4 小时前
微服务swagger解析部署使用全流程
java·微服务·架构·swagger
王彬泽16 小时前
【微服务】组件、基础工程构建(day2)
微服务
Cikiss16 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
Cikiss16 小时前
微服务实战——平台属性
java·数据库·后端·微服务
攸攸太上20 小时前
JMeter学习
java·后端·学习·jmeter·微服务
妍妍的宝贝21 小时前
k8s 中微服务之 MetailLB 搭配 ingress-nginx 实现七层负载
nginx·微服务·kubernetes
架构师吕师傅1 天前
性能优化实战(三):缓存为王-面向缓存的设计
后端·微服务·架构
王彬泽1 天前
【微服务】服务注册与发现、分布式配置管理 - Nacos
微服务·服务注册与发现·分布式配置管理
攸攸太上1 天前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway
一直在进步的派大星2 天前
Docker 从安装到实战
java·运维·docker·微服务·容器