微服务架构实践课程
微服务架构实践课程
微服务架构实践课程 微服务架构实践课程 Modified March 13, 2025 可以看到MarsCode给出了十分详细的实现过程,我们可以参考MarsCode来实现一下 user service/src/main/java/com/devops26/user/repository/UserRepository.java的TODO 以及user service/src/main/java/com/devops26/user/service/impl/UserServiceImpl.java的TODO 最终实现类似如下: 完成后我们马上可以来运行一下看看我们的实现是否正确,重新运行user service,然后打开apifox找到getUserById接口并点击运行,我们将参数值设为1,这是我们刚刚插入数据库的user记录的主键 点击发送,可以看到成功返回了我们刚刚插入到数据库中的信息,userId也的确为1 我们也可以尝试一下将userId参数值设为100,会发现返回的result为null,这是因为当前数据库中并不存在userId为100的数据 5. 接入华为云对象存储服务 注册登录华为云,开通对象存储服务,新建桶如下: 记录endpoint: 点进新建的桶,然后左上角选择概览,记住Endpoint和bucketName(你的桶名称) 右上角点进我的凭证 可以看到MarsCode给出了十分详细的实现过程,我们可以参考MarsCode来实现一下 user service/src/main/java/com/devops26/user/repository/UserRepository.java的TODO 以及user service/src/main/java/com/devops26/user/service/impl/UserServiceImpl.java的TODO 最终实现类似如下: 完成后我们马上可以来运行一下看看我们的实现是否正确,重新运行user service,然后打开apifox找到getUserById接口并点击运行,我们将参数值设为1,这是我们刚刚插入数据库的user记录的主键 点击发送,可以看到成功返回了我们刚刚插入到数据库中的信息,userId也的确为1 我们也可以尝试一下将userId参数值设为100,会发现返回的result为null,这是因为当前数据库中并不存在userId为100的数据 5. 接入华为云对象存储服务 注册登录华为云,开通对象存储服务,新建桶如下: 记录endpoint: 点进新建的桶,然后左上角选择概览,记住Endpoint和bucketName(你的桶名称) 右上角点进我的凭证 问问MarsCode该如何使用Gateway将路径前缀为/user的请求转发给user service,来完成这里的TODO 我们看到MarsCode给出了详细的方案,按照MarsCode给出的方案补全TODO,最终需补全的代码如下: 成功配置完后,我们重新启动gateway service(此时需要保证至少eureka server和user service这两个模块也启动) 我们打开apifox,找到我们的getUserById接口,右上角管理环境将测试环境的url设置为http://localhost:8080 保存后,我们运行getUserById接口,同样查询userId为1的用户信息 可以看到修改端口为8080后,我们还是能够和端口为8081时一样成功访问到user service的接口,说明我们的网关成功将这个请求转发到了user service中。这就意味着,之后我们的所有请求都可以统一发送到8080这个端口中,Gateway会自动帮我们转发到我们所需的微服务后端中。 通过上面的学习,我们对微服务架构的划分以及微服务之间如何通过通信进行协作有了一定的了解,而剩下的开发其实和单体架构的开发差别不大,感兴趣的同学可以继续在MarsCode的帮助下了解我们音乐播放平台的实际业务实现逻辑。 课后练习 1. 在tools service/src/main/java/com/devops26/tools/service/impl/ToolsServiceImpl.java中还有一个TODO 这是一个提取图片主题色的方法,你可以试着在MarsCode的帮助下实现吗? 2. music service这个微服务的粒度似乎不够细,你还能进行进一步拆分吗? 💡 本实践课程为南京大学方乐编写 本实践课程为南京大学方乐编写 课程介绍 • 课程标题:如何用AI指导设计微服务架构的音乐平台后端 • 适合人群:对AI辅助开发感兴趣的新手开发者,最好有SpringBoot相关知识和开发经验 • 课程收获: ◦ 了解并学会使用微服务架构 ◦ 学会如何在MarsCode的帮助下设计系统架构以及解决一些项目中的难题 ◦ 了解对象存储服务 ◦ 了解并学会在项目中简单地集成和使用MySQL ◦ 了解并学会使用Apifox进行简单接口的管理和测试 ◦ 了解并学会使用微服务架构 ◦ 学会如何在MarsCode的帮助下设计系统架构以及解决一些项目中的难题 ◦ 了解对象存储服务 ◦ 了解并学会在项目中简单地集成和使用MySQL ◦ 了解并学会使用Apifox进行简单接口的管理和测试 本期课程材料 • 确保已经安装 IntelliJ IDEA Community Edition + 豆包 MarCode 助手: https://www.marscode.cn/workbench?utm source=school&utm medium=online&utm campaign=lesson IntelliJ IDEA Community Edition 下载链接(进去后往下滑选择免费的社区版) :下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE • 豆包 MarsCode 助教:https://doubao.com/bot/ycPXsB5d • Apifox(用于接口管理与测试):https://apifox.com/ • Navicat Premium Lite (数据库可视化管理工具): https://www.navicat.com.cn/download/navicat premium lite • MySQL的安装教程:https://blog.csdn.net/m0 71422677/article/details/136007088 • 课程代码:https://gitee.com/fangle0111/music player marscode.git 如果有git环境也可以通过git clone获取代码如下 前置基础知识 1. 什么是微服务架构 微服务架构可以理解为把一个大型应用拆解成多个小而独立的“模块”,每个模块都负责应用的一个特定功能,每个服务都独立运行、开发和维护,并通过轻量级设备和HTTP型API进行沟通。例如,一个音乐播放平台可以拆成用户管理、歌曲管理、评论管理等多个独立的小模块,每个模块都可以单独开发、测试、部署和扩展。 与此相比,传统的单体架构就像是一个“大块头”的应用,所有功能都集中在一个大系统中。例如,用户管理、订单处理、支付系统等功能都在同一个代码库中,紧密耦合在一起。 可以简单认为微服务架构就是将单体架构里面不同的功能单独拆出来再专门写一个后端,所以微服务架构里有多个后端。 2. 为什么要使用微服务架构 2.1 微服务架构十分灵活 假设你正在开发一个音乐播放平台,功能包括用户管理、歌曲管理、评论管理等。如果你采用传统的单体架构,所有这些功能都在一个大系统里,修改任何一个功能时,可能会影响整个应用。比如你要改进评论功能,整个系统都需要重新测试和部署,过程非常繁琐,开发效率低。 但是,在微服务架构中: • 用户管理、歌曲管理、评论管理等是独立的微服务,互相之间通过API通信。 • 如果你想修改歌曲管理功能,只需要更新歌曲管理微服务,不需要影响评论管理服务或用户管理服务。歌曲管理服务可以独立测试和部署,其他服务保持不变。 • 你还可以用不同的技术栈来开发这些微服务,比如用户管理服务用Go语言开发,歌曲管理服务用Java开发,这样可以根据需求选择最合适的技术。 2.2 微服务架构容错率高 微服务架构把不同的功能拆分成多个独立的服务,每个服务都有自己独立的逻辑和资源。即使某个微服务(比如评论服务)出现故障,其他微服务(如用户管理、歌曲服务等)仍然可以正常运行,不会受到影响。 就像一个公司里如果财务部门出了问题,其他部门(比如销售、技术)仍然可以继续工作。 3. 微服务架构之间如何通过通信进行协作 HTTP/RESTful API(最常见的方式): • 微服务通常会暴露一个接口(API),其他微服务可以通过HTTP协议访问这些接口进行通信。这就像一个微服务给其他服务提供了一部“电话”,任何服务都可以拨打这个“电话”请求数据或执行操作。 • RESTful API是一种常见的设计方式,它使用简单的HTTP请求(比如GET、POST、PUT、DELETE)来交换数据。数据通常是以JSON格式发送的。 比如: Code block Bash Copy { "code": "200", "msg": "获取用户信息成功!", "result": { "userNO": "1", "name": "user" } 比如: • 消息队列:RabbitMQ、Kafka等消息队列可以用于微服务之间的异步通信。通过发布 订阅模式或点对点模式,微服务可以发送和接收消息,实现解耦和异步处理。 4. 什么是API Gateway 如之前所介绍,采用微服务架构的项目会有很多个后端,也就是说会对外提供许多不同的服务地址,而API Gateway 就是一个统一的入口,用户或应用程序通过它来访问不同的后端服务,而无需直接与这些服务进行直接交互。假设你要进入一个大楼,这个大楼有多个房间,每个房间提供不同的服务(比如餐厅、健身房、商店等)。如果你每次都需要直接去各个房间询问提供的服务,那会很麻烦。API Gateway就像是大楼的一个前台接待员,它负责接待所有来访者(用户请求),根据请求的内容把它们引导到对应的房间(后端服务),并且提供统一的服务。同时,API Gateway 还可以执行身份验证和安全控制,确保只有合法用户能访问特定资源。 5. 对象存储服务 可以认为是一个网盘,可以将文件上传上去,而对象存储服务可以更为直接地向我们提供一个url供我们下载和访问改资源。这也是我们音乐播放平台上传歌曲和相关图片的地方。 常见的对象存储服务: • Amazon S3:亚马逊的对象存储服务,是最知名的一个。 • 阿里云 OSS:阿里巴巴提供的对象存储服务。 • 华为云 OBS:华为提供的对象存储服务。 6. 数据的持久化保存(MySQL) 我们以往在写程序时,可能经常会使用数组等数据结构来存储数据,但当我们的程序停止运行时,这些数据都会随之消失。而MySQL数据库就是用于为我们提供数据的持久化保存,即使程序停止数据也依然还在。 在 MySQL 中,数据通过表来存储,数据被存储在磁盘上,并由数据库管理系统负责管理和查询。MySQL 使用SQL语言来操作数据,包括插入、查询、更新和删除。 实战:设计一个微服务架构的音乐播放平台后端 1. 明确项目需求,根据功能划分微服务 MarsCode提示词参考: 可以看到MarsCode根据我们项目的功能为我们划分为了四个微服务。 实际项目示例代码:https://gitee.com/fangle0111/music player marscode.git 如果有git环境也可以通过git clone获取代码如下 2. 安装MySQL并使用Navicat进行图形化管理 安装MySQL教程:https://blog.csdn.net/m0 71422677/article/details/136007088 打开Navicat,点击左上角的连接 弹出来的窗口选择MySQL,点击右下角下一步 点击确认后即可在左侧看到自己刚刚新建的连接,双击连接本地的mysql数据库,等图标变绿后右键点击连接,选择新建数据库 在弹出的窗口中的数据库名称输入music player,然后直接点击确认即可 这时可以发现我们已经成功建立了数据库music player,双击music player数据库即可对该数据库进行可视化操作了(这里为了方便演示我们所有微服务都共用同一个mysql数据库,实际上微服务架构最好是每一个微服务都单独用一个数据库) 3. IDEA配置与集成Apifox 打开IDEA软件并打开刚刚的示例代码文件,随后点击右上角齿轮 Plugins(插件) 搜索并安装Lombok,Apifox Helper 安装完后点击右下角OK(如果安装完成后有提示需要重启IDEA就重启) 然后打开apifox并登录,点击右上角新建项目,项目类型选择HTTP,名字我这里直接起名为音乐播放器,然后点击新建 然后点击右上角齿轮进入设置,选择API访问令牌,并新建一个,然后复制令牌 回到IDEA,进入设置,点击Apifox Helper 上传到Apifox,并将刚刚复制的令牌输入API访问令牌中 点击测试令牌,成功后点击下面的+,在弹出来的窗口中选择代码模块,点击user service, 之后点开个人空间,选择刚刚我们在apifox中新建的项目 之后点击右下角OK,可以看到我们的代码模块已经和apifox对应起来了,这样就可以很方便的管理接口了 其他代码模块的接口也可以类似导入 接下来打开项目的user service/src/main/java/com/devops26/user/controller/UserController.java 这里是已经写好的提供服务的api接口 右键打开菜单,选择upload to apifox 等待其上传成功如下图后既可以回到apifox中看到示例项目中已经写好的接口了 4. 在项目中集成MySQL并使用JPA进行数据库操作 接下来,让我们尝试在user service中集成MySQL并使用JPA进行数据库操作: MarsCode提示词示例: 可以看到MarsCode给出了清晰的步骤,大家可以尝试着按照MarsCode的提示补全 user service/pom.xml的第一个TODO user service/src/main/resources/application.yml的第一个TODO(注意.properties文件和.yml文件的转换,可以问MarsCode) 在我们的项目中补全完后类似如下: 注意,MarsCode给出的步骤可能会让你引入 如果引入后报错可能是因为这个依赖太旧了已不被你的maven版本支持,可以尝试改为下面的 上面两步完成后,我们就成功集成了MySQL与JPA到项目中 接下来,我们先看看实体类user service/src/main/java/com/devops26/user/entity/User.java,看看一个user中都含有什么属性(可以使用MarsCode的explain功能),当我们引入了jpa并配置了数据库后,我们运行程序时jpa就会自动帮我们在我们指定的数据库中建立相应的user表 打开user service/src/main/java/com/devops26/user/UserApplication.java 点击绿色三角形的run 'user'运行一下user service微服务 当控制台输出显示: 说明运行成功,上面有报错是正常的,因为我们还没有开启eureka服务器,但这不影响最终运行。 打开Navicat,右键点击表并刷新 可以发现user相关的表此时已成功建立 进入user表可以看到我们之前定义的属性 在保持user service运行的情况下,我们尝试往里面插入一条数据,打开apifox,找到save接口,并点击运行 首先,我们先需要配置一下运行环境,点击右上角的请选择环境 选择最下面的管理环境,在打开的窗口中选择测试环境,将前置URL改为如图http://localhost:8081,点击保存并退出 回到运行页面后右上角选择测试环境,然后点击自动生成 将第二行删掉,因为当我们插入新数据时,userId作为主键(我们这里设置的是自增主键)应当由mysql自动生成,删除后点击发送 点击发送后,我们可以发现下面返回了我们插入的内容,还自动帮我们分配了主键userId为1 现在我们打开navicat查看user表,可以看到数据库的user表中确实存在我们刚刚插入的数据 这就是JPA对数据库的操作,接下来,我们将自己动手尝试使用JPA进行一次数据库的查询,我们要实现的是查询特定userId的user数据 MarsCode提示词示例: 在我的凭证页面,单击访问密钥页签,然后单击新增访问密钥 下载凭证后,记住Access Key Id和Secret Access Key 接下来,我们会在我们的示例项目中接入华为云对象存储服务并实现上传文件到bucket然后返回URL 首先,将刚刚的Endpoint, bucketName, Access Key Id和Secret Access Key填入 tools service/src/main/resources/application.yml中 然后,我们找到华为云对象存储服务中关于文件上传的文档文件上传(Java SDK) 对象存储服务 OBS 华为云,找到代码示例并准备基于此实现我们自己的上传功能,但要实现这个功能,我们首先要知道前端传给我们的文件我们应该如何接收,可以问问MarsCode 文件上传(Java SDK) 对象存储服务 OBS 华为云 知道如何接收后,我们就有了接入华为云对象服务并如何改写官方示例代码的方向,打开 tools service/src/main/java/com/devops26/tools/util/ObsUtil.java,我们将要实现这个TODO MarsCode提示词示例: 最终实现参考: 完成后,我们和user service一样,先打开设置将tools service配置到apifox里,然后将ToolsController的接口上传到apifox中以便管理与测试,然后运行tools service 修改测试环境为tools service的端口8084(可以从微服务的application.yml中得知端口号) 上传一个测试文件并点击发送,可以发现上传成功,访问返回的url可以成功下载到该文件 查看华为云的桶中也能发现该文件 这样,我们就可以将歌曲文件,封面,头像等资源存到我们的华为云OBS中,只需把url存到数据库里了 6. 微服务之间的通信 我们知道,微服务虽然是单独测试和部署的,但彼此之间需要通过api通信进行协作,那我们要怎么通信呢,难道和我们在apifox测试一样直接访问ip地址+端口+api接口地址吗,显然,这种方法虽然可行,但在实际实践中十分麻烦,并且,这种方法具有不稳定性:如果服务的 IP 地址发生变化,或者在容器化、Kubernetes 等环境中,IP 地址可能会动态变化。这种方法也难以扩展:服务实例通常不止一个,单纯通过 IP 访问很难处理负载均衡和故障切换。 那有没有更好的方法呢,让我们看看MarsCode怎么说 可以看到,MarsCode为我们提供了许多方案。 6.1 基于Eureka的服务注册和服务发现 MarsCode提示词示例: 可以看到MarCode给出了详细解释,简单来说,通过将微服务注册到eureka中,我们进行微服务之间的通信时就不再需要硬编码网络地址了,可以直接使用服务名,比如原来的http://localhost:8081可以写成http://user service ,这样就可以很好地应对ip地址发送变化时的问题了。但注意这种方式在当前我们的项目配置下只能用于微服务之间的通信,如果从外部访问(比如apifox测试)还是得使用实际的网络地址. 知道了Eureka是干什么的,那么我们要怎么集成使用呢?问问MarsCode 可以看到MarsCode给出了详细的实现方式,大致就是在需要注册的微服务中引入Eureka客户端依赖并进行配置,并且我们也需要启动一个Eureka的服务器。 这些其实在我们的示例代码都已经帮大家配置好了,大家可以对照查看一下代码,其中我们的Eureka服务器就是我们示例代码中的eureka server模块,如果大家看不懂,可以使用MarsCode的解释代码功能配合理解。 了解了eureka后,我们可以马上来实践一下,看看服务是否可以成功注册,我们先启动我们的eureka server,然后启动项目中剩下的所有模块。等所有模块都启动后,我们尝试在浏览器访问一下我们的eureka服务器地址http://localhost:8761 我们可以看到,我们剩下的5个微服务都已经成功的注册到了我们的eureka服务器中。 6.2 使用Feign进行微服务之间的通信 MarsCode提示词示例: 可以看到,Feign的使用使得我们可以像写Controller层接口那样轻松实现微服务之间的通信,大大简化了我们进行HTTP通信的过程。 Feign已经在我们的示例项目中配置好并已经使用了,大家可以对照着MarsCode尝试阅读一下代码。 另外,给有一定开发经验同学们一个小小的思考题:假如前端发来的请求中带有需要我们在业务逻辑中进行解析的请求头(header),比如说代表着发起请求者身份的jwt,我们要怎么保证使用Feign进行微服务之间的通信时也把这个header带上呢? 感兴趣的同学可以在MarsCode的帮助下尝试阅读理解一下我们示例代码中的FeignConfig这个配置类,可以在这里找到答案。 7. 统一处理来自客户端的请求 刚才我们通过服务注册和发现,使得微服务之间可以通过服务名来通信。但这种方式只适用于微服务之间。如果客户端想要请求服务,还是需要通过实际的网络地址来访问我们的服务。而且如果有很多个微服务,就意味着有很多对外提供服务的地址。每个请求过来,我们还得在每个微服务中处理接口鉴权等问题,这给开发带来了很多不便。 因此,我们可以使用我们之前在前置基础知识中讲到到API Gateway统一处理来自客户端的请求。 MarsCode提示词示例: Spring Gateway已经在我们的示例项目中配置好并已经使用了,大家可以对照着MarsCode尝试阅读一下代码,Spring Gateway在我们的gateway service模块。 成功集成了Spring Gateway后,我们就可以统一接受来自客户端的请求并通过微服务之间的通信来转发到对应的微服务后端了,这样我们就又可以使用服务名来代替实际网络地址了,而且我们还只需向客户端暴露一个网络地址,更加安全,另外还能在网关中统一进行接口鉴权等处理,不需要重复地在每一个微服务中进行处理了,更加方便快捷。 现在,让我们上手实践一下来体验一下网关的作用。 打开user service/src/main/java/com/devops26/user/controller/UserController.java 我们可以看到我们user service的请求路径都有一个共同的前缀/user 现在我们打开gateway service/src/main/resources/application.yml