SpringCloud初体验
以一次我们实验室承接的国家级项目实践开发为契机,搞了一点 SpringCloud 相关的微服务系统架构,在此分享。相关的代码我贴在了自己的 Gayhub 上,有需要的话欢迎自取
Presented By AlbertoWang
SpringCloud 可能会涉及到一些 SpringBoot 的基础知识,在过程中会简单提一下,我在代码里写了一些注释来方便理解 SpringBoot 的相关内容,SpringBoot 项目初始化可以参考鄙人的 SpringBoot 初体验
由于采用了微服务,会使得 Maven 项目当中包含若干个服务模块(也就是多个子 Maven 项目),且是多人协同开发,所以 Maven 的版本管理尤为重要
贴一下我的环境吧
本机环境
- JDK: 1.8
- Maven: 3.6.1
- Nacos: 1.2.1
Spring 相关组件版本
- SpringBoot: 2.2.6
- SpringCloud: Hoxton.SR4
- SpringCloud Alibaba: 2.2.0
代码主要用 IDEA 编写,在 macOS 下测试没问题,其他 IDE 或者环境大家自己去试一试
SpringCloud 全家桶
我这里搬运一下 Spring 官网的一些概念,并且根据查到的一些资料大概做一下技术选择
微服务架构图
分布式微服务体系架构如下
相关组件构成
-
服务注册与发现:为了跟踪到其他微服务,可以使用官方提供的
DiscoveryClient
(提供给 Eureka、Consul、Zookeeper等)由于 Eureka 停止更新,目前比较推荐的似乎是阿里的 Nacos,更强的是 Nacos 不仅仅支持服务注册与发现,还提供了动态配置管理、权限管理、消息总线等,可以说是在 SpringCloud 全家桶里的阿里全家桶了
如图安装了 Nacos 之后直接访问 localhost:8848/nacos 就可以管理项目的相关内容(能看到界面的框架比其他框架不知高到哪里去了),初始账号密码都是
nacos
(不知道这个默认端口号是不是彩蛋,没错就是那个 8848 钛金手机🤪) -
服务调用:作用就是把注册号的微服务提供给外部消费者进行使用,对请求量比较多的情况下可能需要采用负载均衡,也就引出了 Ribbon、Feign、OpenFeign 这些组件,这里有他们的对比
不难发现这些组件的包含关系大概是越往后就使用前面的组件并支持一些更好的功能,最明显的就是 OpenFeign 支持 SpringMVC 的相关注解。
但是!!! Nacos 自带集成了 Ribbon,所以说我们使用了 Nacos 就完全不需要管服务调用这方面的事情了
-
服务网关:在微服务架构下,为 Request 和 Response 做正确路由(说白了就是找到前端发来的请求该给谁处理)
比较常用的 Zuul 同样是停止更新,目前推荐的似乎是 Spring 自卖自夸的 Gateway
-
配置管理:Nacos 实际上已经自带了配置管理,引入依赖即可,当然也 Spring 也有自家解决方案 Config,但是既然都用了 Nacos 就送佛送到西,一路用到底吧(这样去面阿里还可以👅一波岂不美哉)
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
初始化 SpringCloud 项目
我这里直接采用官网生成器的方式,左侧是项目相关信息,右侧是 SpringCloud 组件的自助超市
编辑 pom.xml
对子项目依赖的版本号做管理
可能有没怎么用过 Maven 的小伙伴,在这里就简单介绍一些相关需要用到的东西
依赖当中的 dependencyManagement 是给子项目提供版本管理的,在这里声明了版本的依赖,在子项目的 pom.xml
中只需要声明依赖的 groupId 和 artifactId 即可
由于我这里使用的是官网项目生成器生成的 Maven 项目,它已经作为子项目继承了一些组件的版本号
这里是子项目依赖的组件的版本控制,其中 lombok 是一个通过注解自动补全代码的插件,可以自动生成无参、全参构造函数,setter,getter等
为了避免经常重启项目的麻烦(SpringBoot 自带的 Tomcat 比较蠢,开启之后忘记关闭再开一个就会报端口占用,还需要去 kill 进程,每次改了代码还要手动重启 Tomcat 就更容易造成这种问题),可以采用热部署,但是在实际打包发布的时候切记取消
建立子模块
在 Maven 父项目右键: New -> Module -> Maven 建立子模块项目(也可以继续使用官方提供的生成器,我这里采用手动新建的方式做对比)
构建相关业务的包
一般 MVC 架构的 Web 项目可能会包含如下文件夹:
- Controller: 调用相关 Service 响应前端发来的请求,并根据 Controller 的种类返回响应的 JSON 数据 / HTML页面
- Service: 提供给 Controller 的相关业务服务,一般该文件夹内存放 Interface,具体的实现存放在该文件夹的子文件夹
Service/ServiceImpl
内 - Model (Pojo / Entity): 数据库表对应的实体或其他业务相关实体(对象),用来承载数据
- Dao (Mapper): 提供给 ServiceImpl 的接口,用于数据持久化(存到数据库)
认识一下 SpringBoot 的目录结构
这是我以前搞的一个充满 bug 的小项目
-
src/main/java
文件夹下都是和 java 相关的,也就是上面提到的业务相关部分 -
src/main/resources
文件夹内包含配置相关的文件和静态页面的文件application.yml
是 SpringBoot 项目相关的配置文件,包含了自带 Tomcat 端口号、数据库连接、Spring Security配置管理等一系列,反正需要自定义一些东西就是在这里了static
文件夹用来存放一些 CSS / JS 文件,用作 HTML 引用的外部资源templates
文件夹用来存放页面的 HTML 文件
需要注意的是这种方式的静态页面资源需要使用 SpringBoot 提供的 thymeleaf 组件
编写业务代码
这部分我就略过了,为了简单快速搞个 demo ,用 hardcode 的方式伪造了一个 Dao 的接口,各位可以看 cloudservice-provider-user9001
以及 cloudservice-provider-item9002
这两个子项目代码里的注释(最后的部分命名方式为服务名 + 端口号),最后启动服务并访问对应 URI 结果如下,返回了一条 JSON 数据
- PS:由于使用了 Spring Security,在每次启动服务器并发起请求时,默认都会要求进行身份验证,如果未在
application.yml
中特殊指明的话,默认的账号为 user,密码会随机初始化在控制台中,如下
Nacos
关于 Nacos 的部署我这里就不讲了,大家可以参照官方文档进行安装
服务注册与发现
这个东西的流程大概如下图所示,其实也就是消费者服务器对生产者服务器发起 Request 请求它的微服务。这样做的好处就是可以很容易做到负载均衡和系统解耦,关于负载均衡和微服务架构的好处可以参考这个,感觉解释的比较容易理解
构建生产者 Provider
-
上面建立的两个子模块都是作为 provider 也就是服务生产者,但实际上是只是两个独立的 SpringBoot 项目,因此需要在两个子项目的
pom.xml
中添加 Nacos 的服务注册依赖并通过相关注解让他们生效<!-- Nacos注册发现--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
-
在
application.yml
为两个子项目添加 Nacos 地址spring: cloud: nacos: discovery: # Nacos服务注册地址 server-addr: localhost:8848
-
在主函数启动类(
UserServiceMain.java
以及ItemServiceMain.java
)中添加注解@EnableDiscoveryClient
-
重新启动两个子项目,并进入 localhost:8848/nacos 登录查看服务列表,可以发现刚才的两个子服务已经成功注册
进去详情可以看到服务的相关信息
以及可以直接拿来粘贴做服务集成测试的代码
后期的服务管理可以直接用 Nacos 的 Web 管理页面进行,方便的雅痞~
构建消费者 Consumer
由于消费者仅仅是需要调用服务,所以包含的文件夹不再有 Dao、Service 等业务逻辑相关的服务代码,仅仅需要 Controller 来响应请求并调用其他微服务提供的服务模块即可
- 创建一个新子模块(Maven 工程)作为消费者,流程不再赘述
- 在
pom.xml
中添加 OpenFeign 的相关依赖(不要忘记在父工程的pom.xml
中添加版本控制或在本pom.xml
中添加版本号,否则在子项目中会报错)<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
- 添加相关文件
-
由于使用了负载均衡,需要做一个 RestTemplate 的配置文件。关于 RestTemplate 可以参考这个,简单来说这玩意就是用来访问 URI 的,我们用它来访问生产者提供的响应对应服务的 URI,所谓配置其实也只是应用了一些注解
-
在
application.yml
中添加微服务 URI 的地址,主要就是图中圈起来的部分,这部分也可以用 hardcode 的方式写在 Controller 里,但是这样修改起来比较麻烦这里的
nacos-user-service
和nacos-item-service
对应到下面要提到的 Controller 内的常量;两个 URI 分别对应了生产者微服务暴露出来的两个请求(微服务名就是子模块的工程名,与 Nacos 中的相同;请求 URI 可以看两个生产者子模块的 Controller 内的请求 URI,和这里的 URI 是一一对应的) -
Controller 的编码没什么好说的,主要涉及到了使用注解配置 RestTemplate 和将
application.yml
中的配置的生产者微服务 URI 映射到这里的常量,具体可以看我在代码里写的注释 -
在主启动类
DemoConsumerMain
中需要添加 OpenFeign 与 Nacos 注册的注解@EnableDiscoveryClient @EnableFeignClients
-
消费者运行
-
效果
在消费者的
90
端口发起请求,可以直接调用定义在9001
端口的生产者服务,为了进一步显示调用端口的弃坑,我在两个生产者模块里改写了一下伪 Dao ,把服务对应的端口作为数据放入了 JSON 中 -
这里有一些小问题和需要注意的地方
- 在我使用了 Spring Security 之后,如果消费者对生产者进行请求,会报无权限错;去掉了 Spring Security 之后就没这个问题了,大概是各个服务器之间的权限级别问题,这个以后研究研究 Spring Security 再补上
- 不要忘记启动生产者服务器。由于微服务是服务器之间进行请求和应答的,所以把生产者服务器启动并给了消费者服务器权限,消费者服务器才可以对生产则服务器进行请求(调试过程因为手残关错了服务器,这个问题卡了很久,希望大家别像我一样踩这个弱智坑)
- 在我使用了 Spring Security 之后,如果消费者对生产者进行请求,会报无权限错;去掉了 Spring Security 之后就没这个问题了,大概是各个服务器之间的权限级别问题,这个以后研究研究 Spring Security 再补上
-
至此分布式微服务架构的项目大概就搭好了,之后关于 GateWay 和 Spring Security 的内容之后再写个文档。关于服务降级熔断之类的,感觉这次项目也用不上,暂时就先搁置一下,以后再去了解一下