目录
DependencyManagement 和 Dependencies
1.认识微服务
1.1单体架构
很多创业公司早期或者传统企业会把业务的所有功能实现都打包在一个项目,这就是单体架构.
业务的所有功能实现都打包在一个war包或者Jar包中,这种方式就称为单体架构
这种架构开发简单,部署简单,一个项目就包含了所有的功能,省去了多个项目之间的交互和调用消耗. 直接部署在一个服务器即可.
1.2集群和分布式架构
当网站的用户量越来越大,需求也会越来越多,流量也会越来越大,服务可能就会面临以下问题:
- 后端服务器的压力就会越来越大,负载越来越高,甚至出现无法访问的情况
- 业务场景逐渐复杂,为了满足用户的需求,单体应用也会越来越大.各个业务代码之间的耦合度也会越来越高,任何一个问题,都需要整个项目重新构建,发布.
- 一个微小的问题,可能会导致整个应用挂掉
我们从两个方面进行优化:
- 横向:添加服务器,把单台机器变成多台机器的集群.
- 纵向:把一个应用,按照业务进行拆分,拆分为多个项目.此架构也称为垂直架构.
以单体结构规模的项目为单位进行垂直划分.也就是将一个大项目拆分成一个一个单体结构项目.项目和项目之间相对比较独立,接口多为数据同步功能.
集群和分布式:
- 集群(cluster)是将一个系统完整的部署到多个服务器上,每个服务器都能提供系统的所有服务,多个服务器通过负载均衡调度完成任务.每个服务器称为集群的节点(node)
- 分布式是将一个系统拆分为多个子系统,多个子系统部署在多个服务器上,多个服务器上的子系统 协同合作完成一个特定任务.
比如:
一个饭店只有一个厨师,这个厨师负责备菜,洗菜,切菜,炒菜。
随着这个饭店的生意越来越好,这个厨师忙不过来了,饭店又请了一个厨师,新厨师和老厨师做一样的事情,也是洗菜,切菜,炒菜.这两个厨师的关系就是集群.
为了让厨师专心炒菜,饭店又请了一个配菜师,负责备菜,洗菜,切菜.厨师和配菜师的关系就是分布式. 后来一个配菜师也忙不过来了,又请了一个配菜师,这两个配菜师的关系就是集群.
集群和分布式区别和联系:
- 从概念上.集群是多个计算机做同样的事,分布式是多个计算机做不同的事
- 从功能上.集群的每一个节点功能是相同的,并且可以替代.分布式也是多个节点组成的系统,但是每个节点完成的业务是不同的,一个节点出现问题,这个业务就不可访问了
- 从关系上.分布式和集群在实践中,很多时候是互相配合使用的.比如分布式的某一个节点,可能由一个集群来代替.分布式架构大多是建立在集群上的.所以实际的分布式架构设计中并不会把分布式和集群单独区分,而是统称:分布式架构.
1.3微服务架构
按照业务进行拆分后,会有一些重复的功能开发,比如订单系统,电商平台和支付系统都会涉及.
在分布式架构下,当部署的服务越来越多,重复的代码就会越来越多,服务的调用关系也会越来越复杂. 我们可以把一些通用的,会被多个上层服务调用的共享业务,提取成独立的基础服务,组成一个个微小的服务.这就是微服务.
简单来说,微服务就是很小的服务.小到一个服务只对应一个单一的功能,只做一件事.这个服务可以单独部署运行,
微服务之间可以采用REST和RPC协议进行通信.
从这个角度来看,微服务架构是分布式架构的一种拓展,这种架构模式下它拆分粒度更小,服务更独立.可以理解为:微服务是一种经过良好架构设计的分布式架构方案.
分布式架构和微服务架构:
- 分布式:服务拆分,拆了就行.
- 微服务:指非常微小的服务,更细粒度的垂直拆分,通常指不能再拆的服务
- 分布式架构侧重于压力的分散,强调的是服务的分散化.微服务侧重于能力的分散,更强调服务专业化和精细分工.从实践的角度来看,微服务架构通常是分布式服务架构,反之则未必成立.所以,选择微服务 通常意味着需要解决分布式架构的各种难题.
1.4微服务的优势
- 易开发和维护.每个微服务负责的业务比较清晰,体量小,开发和维护成本降低.
- 容错性高.一个服务发生故障,可以使故障隔离在单个服务中,不影响整体服务故障.
- 扩展性好.每个服务都是独立运行的,我们可以结合项目实际情况进行扩展,按需伸缩.
- 技术选型灵活.每个微服务都是单独的团队来运维,可以根据业务特点和团队特点,选择适合的技术栈.
2.微服务解决方案-Spring Cloud
2.1什么是Spring Cloud
SpringCloud提供了一些可以让开发人员快速构建分布式服务的工具,比如配置管理,服务发现,熔断,智能路由等.他们可以在任何分布式环境中很好的工作.
SpringCloud就是分布式微服务架构的一站式解决方案,是微服务架构落地的多种技术的集合.
- 比如:
- Distributed/versioned configuration分布式版本配置
- Service registration and discovery服务注册和发现
- Routing路由
- Service-to-service calls服务调用
- Load balancing负载均衡
- Circuit Breakers断路器
- Distributed messaging分布式消息
- .......
Spring Cloud和SpringBoot的关系
SpringCloud中的所有子项目都依赖SpringBoot,所以SpringBoot和Spring Cloud的版本之间也存在 一定的对应关系
比如SpringBoot 3.2.X对应的SpringCloud版本是2023.0.X
2.2Spring Cloud Alibaba
SpringCloudAlibaba是阿里巴巴集团下的开源组件和云产品在SpringCloud规范下的实现.
虽然SpringCloud Alibaba目前并不是SpringCloud官方推荐的默认方案,但是Spring Cloud Alibaba 是阿里中间件团队主导的一个新生项目,正处于高速迭代中.甚至在Alibaba的开源组件还没有织入 SpringCloud生态之前,就已经在各大公司广泛使用了.
官方网站: Spring Cloud Alibaba
Spring Cloud Alibaba吸收了Spring Cloud Netflix微服务框架的核心架构思想,并进行了高性能改 进.自SpringCloud Netflix进入停更维护后,SpringCloud Alibaba逐渐代替它成为主流的微服务框架.
2.3SpringCloud实现对比
3.服务拆分原则
微服务到底多小才算"微",这个在业界并没有明确的标准.微服务并不是越小越好,服务越小,微服务架构的优点和缺点都会越来越明显.
服务越小,微服务的独立性就会越来越高,但同时,微服务的数量也会越多,管理这些微服务的难度也会提高.所以服务拆分也要考虑场景.
以企业管理为例
企业中一个员工的工作内容与企业规模,项目规模等都有关系.
在小公司,一个员工可能需要负责很多部门的事情,大公司的话,一个部门的工作可能需要多个员工来处理.
拆分微服务一般遵循如下原则:
3.1单一职责原则
单一职责原则原本是面向对象设计中的一个基本原则,它指的是一个类应该专注于单一功能.不要存在 多于一个导致类变更的原因.
在微服务架构中,一个微服务也应该只负责一个功能或业务领域,每个服务应该有清晰的定义和边界,只 关注自己的特定业务领域.
组织团队也是,一个人专注做一件事情的效率远高于同时关注多件事情.
比如一个人同时管理和维护一份代码,要比多个人同时维护多份代码的效率高.
如电商系统
3.2服务自治原则
服务自治是指每个微服务都应该具备高度自治的能力,即每个服务要能做到独立开发,独立测试,独立构建,独立部署,独立运行.
以上面的电商系统为例,每一个微服务应该有自己的存储,配置,在进行开发,构建,部署,运行和测试时,并不需要过多关注其他微服务的状态和数据
3.3单向依赖原则
微服务之间需要做到单向依赖,严禁循环依赖,双向依赖
循环依赖:A->B->C->A
双向依赖:A->B,B->A
如果一些场景确实无法避免循环依赖或者双向依赖,可以考虑使用消息队列等其他方式实现
目的与优势:
- 降低耦合度:通过确保依赖的单向性,减少了模块之间的直接耦合,使得系统更加灵活和易于扩展。
- 提高可维护性:当某个模块发生变化时,由于依赖关系的单向性,这种变化对依赖它的模块影响较小,从而降低了维护成本。
- 促进模块化设计:单向依赖原则鼓励开发者将系统划分为多个相对独立的模块,每个模块负责特定的功能,提高了系统的模块化程度。
3.4服务拆分示例
以订单列表为例:
简单来看,这个页面提供了以下信息
1.订单列表 2.商品信息
根据服务的单一职责原则,我们把服务进行拆分为:订单服务,商品服务
订单服务:提供订单ID,获取订单详细信息
商品服务:根据商品ID,返回商品详细信息。
4.基本工程搭建
4.1创建父子工程
在父工程SpringCloud中新建两个模块:order-service 和 product-service
4.2完善pom文件
使用properties来进行版本号的统一管理
使用dependencyManagement来管理依赖,声明父工程的打包方式为pom
父工程的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>order-service</module>
<module>product-service</module>
</modules>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
<mybatis.version>3.0.3</mybatis.version>
<mysql.version>8.0.33</mysql.version>
<spring-cloud.version>2022.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>${mybatis.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
子工程的pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SpringCloudDemo</artifactId>
<groupId>com.example</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>order-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
DependencyManagement 和 Dependencies
dependencies:将所依赖的jar直接加到项目中.子项目也会继承该依赖。
dependencyManagement:只是声明依赖,并不实现Jar包引入.如果子项目需要用到相关依赖, 需要显式声明.如果子项目没有指定具体版本,会从父项目中读取version.如果子项目中指定了版本 号,就会使用子项目中指定的jar版本.此外父工程的打包方式应该是pom,不是jar,这里需要手动 使用packaging来声明.
SpringBoot实现依赖jar包版本的管理,也是这种方式
4.3远程调用
根据订单查询订单信息时,根据订单里产品ID,获取产品的详细信息.
实现思路:order-service服务向product-service服务发送一个http请求,把得到的返回结果,和订单结果融合在一起,返回给调用方.
实现方式:采用Spring提供的RestTemplate
实现http请求的⽅式, 有很多, 可参考:http请求实现
5.RestTemplate
1.定义RestTemplate
@Configuration
public class BeanConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.修改order-service
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://127.0.0.1:9090/product/"+orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
访问url: http://127.0.0.1:8080/order/1
页面返回结果:
5.1什么是REST?
REST(Representational State Transfer),表现层资源状态转移.
REST是由HTTP的主要设计者Roy Fielding博士在他的博士论文中提出来的一种软件架构风格.
这里面主要有三个概念:
- 1.资源:网络上的所有事物都可以抽象为资源,每个资源都有一个唯一的资源标识符(URI)
- 2.表现层:资源的表现形式,比如文本作为资源,可以用txt格式表现,也可以通HTML,XML,JSON等格式来表现,甚至以二进制的格式表现.
- 3.状态转移:访问URI,也就是客户端和服务器的交互过程.客户端用到的手段,只能是HTTP协议.这 个过程中,可能会涉及到数据状态的变化.比如对数据的增删改查,都是状态的转移.
REST是一种设计风格,指资源在网络中以某种表现形式进行状态转移.
简单来说:REST描述的是在网络中Client和Server的一种交互形式,REST本身不实用,实用的是如何设计RESTful API(REST风格的网络接口)
6.项目存在问题
远程调用时,URL的IP和端口号是写死的(http://127.0.0.1:9090/product/),若更换IP,需要修改代码
调用方如何可以不依赖服务提供方的IP? 多机部署,如何分摊压力?
远程调用时,URL非常容易写错,而且复用性不高,如何优雅的实现远程调用 所有的服务都可以调用该接口,是否有风险?
欲知后事如何,请看下集