前言:哈喽,各位 CSDN 的技术伙伴们~👋

最近博主在学习微服务相关内容,踩了不少拆分和治理的坑,也整理了一套从核心思想到落地实操的完整流程。不管你是刚接触微服务的新手,还是正在项目中面临拆分难题的开发者,这篇文章都会从「是什么 - 为什么 - 怎么做」三个维度,把微服务的拆分逻辑、工程结构、服务注册发现等关键知识点讲透,还附带 Nacos、OpenFeign 的具体配置代码,直接复制就能用!话不多说,咱们正文开始~

微服务的拆分与治理:从思想到落地

一、微服务核心思想

1.1 什么是微服务

微服务架构,是服务化指导思想下的一套最佳实践架构方案。
微服务化,就是把这个单体项目拆分为多个独立的项目

1.2 使用微服务的好处

粒度小:每个模块仅负责自己单独的功能(登录功能、用户管理功能)
团队自治:每个模块都会有一个几人或十几人的小团队去管理
服务自治:测试以及部署的时候每个模块可以单独部署,拥有自己的数据库

使用微服务,可以极大地优化项目打包构建时间,还可以有效减轻数据库的访问压力

二、服务拆分

2.1 SpringCloud官网

Spring Cloud 官方网站为:https://spring.io/projects/spring-cloud
注:springboot3.0.x以上需要使用2022.0.x的版本

2.2 拆分思想与原则

2.2.1 如何拆分

从拆分目标上来说:
高内聚:每个微服务职责单一,包含的业务关联性高
低耦合:每个微服务之间尽量独立,尽可能减少对其他微服务模块或项目的影响
从拆分维度上来说:
横向拆分:抽取公共服务,提高复用性(登录以及订单功能的风控)
纵向拆分:按照业务模块拆分(分布式)

2.2.2工程结构种类
  1. 独立project:
    每块业务逻辑都是一个独立的项目,有多少个微服务,就需要new多少个项目,最后再把这一堆项目扔到一个统一的目录里面去做一个松散的管理。
  2. maven聚合:
    每块业务逻辑都是一个独立的模块,有多少个微服务,就需要new多少个模块,统一交给一个maven项目做一个聚合型管理。适用于中小型项目

2.3 maven聚合形式的拆分

2.3.1拆分流程

前提:有一个完善的、可以正常工作的单体项目
方法:

  1. 创建一个maven包(不是spring项目),起名为xxx-service
  2. 复制单体架构项目的maven内容,根据需求修改为一个适合的,足够该微服务使用的pom文件
  3. 复制单体架构项目里面的yml文件,修改为一个新的端口号,修改数据库配置
  4. 写好三层架构、domain包等,以及resource里面的mapper,内容从单体架构中对应的包里面复制即可
2.3.2远程调用

有些微服务在拆分的时候,由于原先在单体项目中作为一个模块它会调用其他其他模块的service方法来完成相关功能,但是现在由于各服务之间相互隔离,无法调用。
它们虽然端口号不同,但是都在同一个网络下,可以通过网络沟通
解决方法

  1. 使用RestTemplate(不推荐,请求方式繁琐+路径写死)
  2. 使用OpenFeign(服务治理内容)

三、服务治理

3.1 服务注册

通常情况下,大型项目为了解决高并发下的服务器卡顿,缓解服务器压力,这些易引发高并发的模块会被使用多个docker容器进行部署管理
服务治理下的三个角色

  • 服务提供者:暴露服务接口,便于其他服务调用
  • 服务消费者:调用其他服务的接口
  • 注册中心:记录并监控各个微服务实例的状态,并推送服务变更信息
    注册中心原理
  1. 与nginx的负载均衡的原理相似,
    相当于有一个中间商做代理,对于该服务所访问的一些其他服务,这个代理能根据需要给出几个合适的服务,然后通过随机、轮询、哈希、加权随机等形式来处理(挨个试),直到找到首个可以满足要求的服务,把对应的ip地址给了这个访问服务,让它拿着这个ip地址对指定服务进行远程调用,进而访问到指定服务的指定接口,并获取到返回的数据
  2. 所有服务都是会定期向注册中心发送注册服务信息的(让中间商知道自己还活着)(信息内容:包括 IP、端口、服务名、健康检查路
    径等),在服务运行过程中,可能某个服务因为某些原因突然挂掉了,没有继续发服务信息,服务中心就会认为该服务已经出现故障,就会从注册中心中将该服务剔除(心跳机制
  3. 剔除挂掉的服务之后,注册中心还会告知调用者对应的哪一个服务已经挂掉了,不能再访问了(推送变更
  4. 如果这个服务复活了,或者又引进来了新的服务,注册中心又会有新的变动,时刻告知调用者对应服务的最新状态
    注册中心种类
  • Eureka:AP 模型(高可用优先),客户端拉取服务列表,心跳默认 30 秒,剔除默认 90 秒。
  • Consul:CP 模型(强一致性优先),支持多数据中心,内置健康检查。
  • Nacos:同时支持 AP/CP 模型,兼容 Dubbo、Spring Cloud,国内使用广泛。
    注册中心的搭建
  1. 下载nacos数据库脚本(使用官方脚本)
    nacos/distribution/conf/mysql-schema.sql at master · alibaba/nacos
  2. 创建专用网络,保证各个微服务、数据库以及nacos在同一网络下。
  3. 使用docker创建nacos专属mysql容器,加载脚本生成nacos数据库
  4. 创建nacos的docker容器
docker run -d \
  -p 8848:8848 -p 9848:9848 -p 9849:9849 \
  --name nacos \
  --network hmall-spring-cloud \ # 与MySQL容器在同一网络
  -e MODE=standalone \ # 单机模式
  -e SPRING_DATASOURCE_PLATFORM=mysql \ # 指定使用mysql数据库
  -e MYSQL_SERVICE_HOST=nacos-mysql \ # 创建的nacos的mysql容器名
  -e MYSQL_SERVICE_PORT=3306 \ #mysql默认端口
  -e MYSQL_SERVICE_DB_NAME=nacos \ # 创建的nacos的mysql容器中存放nacos数据的数据库
  -e MYSQL_SERVICE_USER=root \ # 数据库用户名
  -e MYSQL_SERVICE_PASSWORD=159357 \ # 密码
  -e MYSQL_DATABASE_NUM=1 \ # 数据库实例数量:单机为1
  nacos/nacos-server:2.1.0

注:nacos个人开发推荐使用2.1.0以下版本,新版配置繁琐。使用时去掉注释
5. 使用:ip地址:8848/nacos访问私网,用户名&密码:nacos

在maven聚合项目中进行服务注册

  1. 引入 nacos discovery 依赖:
<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  1. 配置 Nacos 地址
spring:
  application:
    name: xxx-service # 服务名称
  cloud:
    nacos:
      server-addr: xxxxxxxxxx:8848 # nacos地址

这个注册需要对所有微服务模块做

3.2 服务发现

使用OpenFeign,OpenFeign内部已经内置了服务发现功能
实践方案
根据项目的工程结构进行合理选择

  1. 对于独立project:
  • 把原来的被调用的微服务再拆成三个模块
  • xxx-dto放实体类,xxx-api放httpclient接口,xxx-biz放业务逻辑代码
    优点:
  • 项目耦合度更低
  • httpClient接口由负责该微服务部分的团队编写,该团队对该接口的熟悉度更高
    缺点:项目结构更加复杂
  1. 对于maven聚合:
    创建一个新的包,里面放所有微服务想要暴露的客户端
    优点:代码简洁且通用性更强
    缺点:代码耦合度更高

在maven聚合项目中进行服务发现

  1. 创建xxx-api包,下设三个包,client、config、dto
  2. client包中是需要被共享使用的接口xxxClient,config里面放可能用到的配置文件,dto里面放共用的dto类
    client类的编写示例:
@FeignClient(value = "item-service") //value后面是被调用服务名称
public interface ItemClient {
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
  1. 修改xxx-api的pom文件,把openfeign和负载均衡的依赖引入hm-api的pom文件中
<!--OpenFeign-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

由于openfeign的底层使用的是loadbalancer,所以必须引入负载均衡依赖

  1. 对于调用者,引入OKHttp的依赖
<!--ok-http-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

开启okhttp提供的连接池功能

feign:
  okhttp:
    enabled: true # 开启OKHttp连接池支持

并引入xxx-api包(通过依赖传递可以让调用者获得相关依赖)
5. 给cart-service的启动类上的注解@EnableFeignClients添加扫描"com.hmall.api.client",不然识别不到
6. 业务逻辑中,就可以直接改用xxxClient进行调用
示例:

List<ItemDTO> items = itemClient.queryItemByIds(List.of(1,2,3));

注:编写过程中,如果发现dto冲突,需要删除本服务下的dto,然后使用引入的xxx-api包中共享的dto类
补充:OpenFeign日志输出
四种级别:

  • NONE:不打印任何日志(默认级别,生产环境推荐)
  • BASIC:仅打印请求方法、URL、响应状态码、执行耗时(最轻量化,生产排查基本问题可用)
  • HEADERS:在 BASIC 基础上,额外打印请求 / 响应的头信息
  • FULL:打印请求 / 响应的头、请求体、响应体、状态码、耗时等所有细节(开发调试专用,信息最完整)
  1. 在hm-api中创建配置类DefaultFeignConfig声明一个类型为 Logger.Level 的 Bean,在其中定义日志级别:
public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
}

@Configuration注解可加可不加,加上之后,所有FeignClient都会使用这个级别的日志;不加,则需要显示指定使用
2. 在 @FeignClient 注解中声明,让这个Bean生效:
局部(不加@Configuration):
对于每一个FeignClient,需要显示指定

@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)

全局(加@Configuration):
无需声明,全局生效
3. 配置yml文件中的logging部分:

# yml配置(推荐)
logging:
  level:
    # 格式:logging.level.【Feign客户端接口的全类名】= debug
    com.xxx.feign.UserFeignClient: debug # 局部生效:仅指定接口打印日志
    # 或全局生效:匹配所有Feign客户端(包路径根据自己项目调整)
    com.xxx.feign: debug

结尾:技术交流与总结

以上就是微服务拆分与治理的核心内容啦!从思想到落地,每一步都附了实操代码,希望能帮大家少走弯路~ 如果在实操过程中遇到 Nacos 启动失败、Feign 调用超时、依赖冲突等问题,或者有更好的拆分思路,欢迎在评论区留言交流!🙌

觉得文章有用的话,别忘了点赞 + 收藏 + 关注呀,后续还会更新微服务、JVM、多线程等更多内容,咱们下期见~

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐