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

前言

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

系统要求如下:

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

  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层提供给内部服务调用,比如架构图中的其他服务
关系还是有点乱的,直接一图解千愁吧!

client调用图

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

分模块架构

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

极度高耦合:(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揭秘,轻松搞定微服务通信!》

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

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

微服务架构

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

在这里插入图片描述

依赖关系使用Maven即可实现

	<!-- 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模块中,于是我们终于得到最终的架构图了!

在这里插入图片描述

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

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值