目录
SpringCloud概述:
1.认识微服务:
问题:
当⽹站的⽤⼾量越来越⼤, 需求也会越来越多, 流量也会越来越⼤, 服务可能就会⾯临以下问题:
• 后端服务器的压⼒就会越来越⼤, 负载越来越⾼, 甚⾄出现⽆法访问的情况
• 业务场景逐渐复杂. 为了满⾜⽤⼾的需求, 单体应⽤也会越来越⼤. 各个业务代码之间的耦合度也会越来越⾼. 任何⼀个问题, 都需要整个项⽬重新构建, 发布.
• ⼀个微⼩的问题, 可能会导致整个应⽤挂掉
解决方案:
我们从两个⽅⾯进⾏优化:
• 横向: 添加服务器, 把单台机器变成多台机器的集群.
• 纵向: 把⼀个应⽤, 按照业务进⾏拆分, 拆分为多个项⽬. 此架构也称为垂直架构.
集群和分布式:
• 集群(cluster)是将⼀个系统完整的部署到多个服务器上, 每个服务器都能提供系统的所有服务, 多个服务器通过负载均衡调度完成任务. 每个服务器称为集群的节点(node)
• 分布式是将⼀个系统拆分为多个⼦系统,多个⼦系统部署在多个服务器上,多个服务器上的⼦系统协同合作完成⼀个特定任务.
集群和分布式区别和联系:
1. 从概念上. 集群是多个计算机做同样的事, 分布式是多个计算机做不同的事
2. 从功能上. 集群的每⼀个节点功能是相同的, 并且可以替代的. 分布式也是多个节点组成的系统, 但是每个节点完成的业务是不同的, ⼀个节点出现问题, 这个业务就不可访问了.
3. 从关系上. 分布式和集群在实践中, 很多时候是互相配合使⽤的. ⽐如分布式的某⼀个节点, 可能由⼀个集群来代替. 分布式架构⼤多是建⽴在集群上的. 所以实际的分布式架构设计中并不会把分布式和集群单独区分, ⽽是统称: 分布式架构.
2.微服务架构:
按照业务进⾏拆分后, 会有⼀些重复的功能开发, ⽐如订单系统, 电商平台和⽀付系统都会涉及.
在分布式架构下, 当部署的服务越来越多, 重复的代码就会越来越多, 服务的调⽤关系也会越来越复杂. 我们可以把⼀些通⽤的, 会被多个上层服务调⽤的共享业务, 提取成独⽴的基础服务, 组成⼀个个微⼩的服务. 这就是微服务.
微服务就是很⼩的服务. ⼩到⼀个服务只对应⼀个单⼀的功能, 只做⼀件事. 这个服务可以单独部署运⾏。从这个⻆度来看, 微服务架构是分布式架构的⼀种拓展, 这种架构模式下它拆分粒度更⼩, 服务更独⽴.可以理解为: 微服务是⼀种经过良好架构设计的分布式架构⽅案
分布式架构侧重于压⼒的分散, 强调的是服务的分散化. 微服务侧重于能⼒的分散, 更强调服务的专业化和精细分⼯. 从实践的⻆度来看, 微服务架构通常是分布式服务架构, 反之则未必成⽴. 所以, 选择微服务通常意味着需要解决分布式架构的各种难题.
优势:
• 易开发和维护. 每个微服务负责的业务⽐较清晰, 体量⼩, 开发和维护成本降低.
• 容错性⾼. ⼀个服务发⽣故障, 可以使故障隔离在单个服务中, 不影响整体服务故障.
• 扩展性好. 每个服务都是独⽴运⾏的, 我们可以结合项⽬实际情况进⾏扩展, 按需伸缩.
• 技术选型灵活. 每个微服务都是单独的团队来运维, 可以根据业务特点和团队特点, 选择适合的技术栈.
挑战:
虽然微服务具备很多的优势, 但由于服务数的增加, 服务治理也是我们⾯临的巨⼤挑战.
• 服务依赖. 随着服务的数量增多, 服务之间的关系也会变得更加复杂. ⼀个服务的更改, 需要考虑对其他服务的影响.
• 运维成本. ⼀个业务流程会涉及多个微服务共同完成, 有更多的服务需要编译, 部署, 运⾏, 甚⾄可能是不同的编程语⾔, 不同的运⾏环境, 当然也需要集群来处理故障转移等. 这对于运维⼈员⽽⾔, 挑战是巨⼤的.
• 开发和测试. ⼀个业务流程可能涉及多个微服务共同完成, 服务调⽤引⼊⽹络延迟, 不可靠的⽹络, 如何进⾏容错处理等问题. 这对开发和测试⽽⾔, 难度也会提升.
• 服务监控. 在⼀个单体结构中, 很容易实现服务的监控. 因为所有功能都在⼀个服务中, 微服务架构下, 不仅需要对整个链路进⾏监控, 还需要对每⼀个服务实现监控.
• 负载均衡. 微服务架构中的服务实例数量可能⾮常庞⼤,因此需要有效的服务发现和负载均衡机制来管理请求流量和保证⾼可⽤性
3.微服务解决方案-SpringCloud介绍:
Spring Cloud 就是分布式微服务架构的⼀站式解决⽅案, 是微服务架构落地的多种技术的集合.
⽐如:
• Distributed/versioned configuration 分布式版本配置
• Service registration and discovery 服务注册和发现
• Routing 路由
• Service-to-service calls 服务调⽤
• Load balancing 负载均衡
• Circuit Breakers 断路器
• Distributed messaging 分布式消息( 比如说使用RabbitMQ )
( 注:Spring Cloud 并不是Spring 团队研发的框架, 它只是把⼀些⽐较优秀的解决微服务架构中常⻅问题的开源框架基于SpringCloud规范进⾏了整合, 并基于SpringBoot的⻛格,对这些组件进⾏封装, 屏蔽掉了复杂的配置和实现原理. 为开发者提供了开箱即⽤的微服务开发体验.这些开源技术的框架是由各个公司来维护的. Spring Cloud 就是这些微服务的⼤管家. )
Spring Cloud和SpringBoot的关系
Spring Cloud中的所有⼦项⽬都依赖SpringBoot, 所以SpringBoot 和Spring Cloud的版本之间也存在⼀定的对应关系
4.SpringCloud的实现方案:
在Spring Cloud的规范下, 有很多实现, 其中最为出名的是
• Spring Cloud Netflix
• Spring Cloud Alibaba
1.Spring Cloud Netflix
Spring Cloud Netflix是 Netflix OSS(Netflix Open Source Software)在Spring Cloud规范下的实现. 包含的组件及其主要功能⼤致如下:
• Eureka: 服务注册和发现
• Zuul: 服务⽹关
• Ribbon: 负载均衡
• Feign: 服务调⽤组件
• Hystrix: 断路器, 提供服务熔断和限流
• Hystrix Dashboard: 监控⾯板
现在已经有很多功能不持续更新,所以SpringCloud官方自己进行了开发
1.Spring Cloud Alibaba:
Spring Cloud Alibaba 吸收了 Spring Cloud Netflix 微服务框架的核⼼架构思想, 并进⾏了⾼性能改进. ⾃ Spring Cloud Netflix 进⼊停更维护后, Spring Cloud Alibaba 逐渐代替它成为主流的微服务框架.
| SpringCloud 官方 | Spring Cloud Netflix | Spring Cloud Alibaba | |
|---|---|---|---|
| 服务注册 / 发现 | Eureka | Eureka | Nacos |
| 服务调用 | OpenFeign | Feign | Dubbo |
| 配置中心 | SpringCloudConfig | Archaius | Nacos |
| 服务网关 | SpringCloudGateway | Zuul | SpringCloudGateway |
| 负载均衡 | SpringCloud LoadBalance | Ribbon | Dubbo |
黄色的已经进入维护
环境和工程的搭建:
1.开发环境安装:
1.JDK17:

这个比较简单,下载完成之后配置环境变量就可以
Windows:

Ubuntu:
连接上服务器后:
sudo apt update
sudo apt install openjdk-17-jdk
检验使用java -version 出现下面状态就可以
2.MySQL:
ubuntu:
- 安装MySQL
- 检验是否安装成功
- 设置MySQL的安全属性
- 登录MySQL
- 更改登陆密码
apt list |grep "mysql-server"
sudo apt install mysql-server
sudo systemctl status mysql
sudo mysql_secure_installation
sudo mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY
'你的新密码';
2.电商平台演示工程:
服务拆分:
拆分微服务—般遵循如下原则:
一定要避免过度设计,合适才是最好的
1. 单⼀职责原则:
单⼀职责原则原本是⾯向对象设计中的⼀个基本原则, 它指的是⼀个类应该专注于单⼀功能. 不要存在多于⼀个导致类变更的原因.在微服务架构中, ⼀个微服务也应该只负责⼀个功能或业务领域, 每个服务应该有清晰的定义和边界, 只关注⾃⼰的特定业务领域.
2. 服务⾃治(不一定严格要求,具体场景具体分析)
服务⾃治是指每个微服务都应该具备⾼度⾃治的能⼒, 即每个服务要能做到独⽴开发, 独⽴测试, 独⽴构建, 独⽴部署, 独⽴运⾏.
以上⾯的电商系统为例,每⼀个微服务应该有⾃⼰的存储, 配置,在进⾏开发, 构建, 部署, 运⾏和测试时,并不需要过多关注其他微服务的状态和数据
3. 单向依赖
微服务之间需要做到单向依赖, 严禁循环依赖, 双向依赖
循环依赖: A -> B -> C ->A
双向依赖: A -> B, B -> A
以订单列表为例:
简单来看, 这个⻚⾯提供了以下信息:
1. 订单列表
2. 商品信息

根据服务的单⼀职责原则, 我们把服务进⾏拆分为: 订单服务, 商品服务
订单服务: 提供订单ID, 获取订单详细信息
商品服务: 根据商品ID, 返回商品详细信息.
数据准备:
订单服务:
-- 建库
create database if not exists cloud_order charset utf8mb4;
-- 订单表
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '订单id',
`user_id` BIGINT ( 20 ) NOT NULL COMMENT '⽤⼾ID',
`product_id` BIGINT ( 20 ) NULL COMMENT '产品id',
`num` INT ( 10 ) NULL DEFAULT 0 COMMENT '下单数量',
`price` BIGINT ( 20 ) NOT NULL COMMENT '实付款',
`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '订单表';
-- 数据初始化
insert into order_detail (user_id,product_id,num,price)
values
(2001, 1001,1,99), (2002, 1002,1,30), (2001, 1003,1,40),
(2003, 1004,3,58), (2004, 1005,7,85), (2005, 1006,7,94);
create database if not exists cloud_product charset utf8mb4;
-- 产品表
DROP TABLE IF EXISTS product_detail;
CREATE TABLE product_detail (
`id` INT NOT NULL AUTO_INCREMENT COMMENT '产品id',
`product_name` varchar ( 128 ) NULL COMMENT '产品名称',
`product_price` BIGINT ( 20 ) NOT NULL COMMENT '产品价格',
`state` TINYINT ( 4 ) NULL DEFAULT 0 COMMENT '产品状态 0-有效 1-下架',
`create_time` DATETIME DEFAULT now(),
`update_time` DATETIME DEFAULT now(),
PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '产品表';
-- 数据初始化
insert into product_detail (id, product_name,product_price,state)
values
(1001,"T恤", 101, 0), (1002, "短袖",30, 0), (1003, "短裤",44, 0),
(1004, "卫⾐",58, 0), (1005, "⻢甲",98, 0),(1006,"⽻绒服", 101, 0),
(1007, "冲锋⾐",30, 0), (1008, "袜⼦",44, 0), (1009, "鞋⼦",58, 0),
(10010, "⽑⾐",98, 0)
工程搭建:
构建⽗⼦⼯程:
创建⽗⼯程:
创建⼀个空的Maven项⽬, 删除所有代码, 只保留pom.xml
各种依赖使用是什么版本要看spingboot是什么版本要对应
完善pom.xml文件:
<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>
引入SpringBoot,java,mybatis,mysql,spring-cloud相应的版本
DependencyManagement 和 Dependencies:
1. dependencies :将所依赖的jar直接加到项⽬中. ⼦项⽬也会继承该依赖.
2. dependencyManagement :只是声明依赖, 并不实现Jar包引⼊. 如果⼦项⽬需要⽤到相关依赖,需要显式声明. 如果⼦项⽬没有指定具体版本, 会从⽗项⽬中读取version. 如果⼦项⽬中指定了版本号,就会使⽤⼦项⽬中指定的jar版本. 此外⽗⼯程的打包⽅式应该是pom,不是jar, 这⾥需要⼿动使⽤ packaging 来声明.
创建子项目:

根据这种方式,创建两个子项目order-service,product-service

然后父工程的pom.xmi就会多:

来表示它们之间的关系,那么项目的结构就已经搭建起来了
order-service的声明项目依赖和项目构建插件:
<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>
order-service的声明项目依赖和项目构建插件:
<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>
订单服务:
功能:通过ID拿到订单order详情

具体的实现非常简单,所以就不演示了
商品服务:

也是如此
在订单服务中通过http请求获取商品信息:使用RestTemplate
对于第三方的类需要使用@Bean注解
@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:8081/product/"+orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
RestTemplate:
REST(Representational State Transfer), 表现层资源状态转移
REST 是⼀种设计⻛格, 指资源在⽹络中以某种表现形式进⾏状态转移.
简单来说: REST描述的是在⽹络中Client和Server的⼀种交互形式, REST本⾝不实⽤,实⽤的是如何设计 RESTful API(REST⻛格的⽹络接⼝)
RESTful ⻛格⼤致有以下⼏个主要特征:
1. 资源: 资源可以是⼀个图⽚, ⾳频, 视频或者JSON格式等⽹络上的⼀个实体, 除了⼀些⼆进制的资源外普通的⽂本资源更多以JSON为载体、⾯向⽤⼾的⼀组数据(通常从数据库中查询⽽得到)
2. 统⼀接⼝: 对资源的操作. ⽐如获取, 创建, 修改和删除. 这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE⽅法. 换⾔⽽知,如果使⽤RESTful⻛格的接⼝, 从接⼝上你可能只能定位其资源,但是⽆法知晓它具体进⾏了什么操作,需要具体了解其发⽣了什么操作动作要从其HTTP请求⽅法类型上进⾏判断
这些内容都是通过HTTP协议来呈现的. 所以RESTful是基于HTTP协议的.
RestTemplate 是Spring提供, 封装HTTP调⽤, 并强制使⽤RESTful⻛格. 它会处理HTTP连接和关闭, 只需要使⽤者提供资源的地址和参数即可.
RESTful实践:
RESTful⻛格的API 固然很好很规范, 但⼤多数互联⽹公司并没有按照其规则来设计, 因为REST是⼀种⻛格,⽽不是⼀种约束或规则, 过于理想的RESTful API 会付出太多的成本.
RESTful API 缺点:
1. 操作⽅式繁琐, RESTful API通常根据GET, POST, PUT, DELETE 来区分对资源的操作动作. 但是
HTTP Method 并不可直接⻅到, 需要通过抓包等⼯具才能观察. 如果把动作放在URL上反⽽更加直
观, 更利于团队的理解和交流.
2. ⼀些浏览器对GET, POST之外的请求⽀持不太友好, 需要额外处理.
3. 过分强调资源. ⽽实际业务需求可能⽐较复杂, 并不能单纯使⽤增删改查就能满⾜需求, 强⾏使⽤RESTful API会增加开发难度和成本.所以, 在实际开发中, 如果业务需求和RESTful API不太匹配或者很⿇烦时, 也可以不⽤RESTful API. 如果使⽤场景和REST⻛格⽐较匹配, 就可以采⽤RESTful API.
总之: ⽆论哪种⻛格的API, 都是为了⽅便团队开发, 协商以及管理, 不能墨守成规. 尽信书不如⽆书, 尽信规范不如⽆规范
项⽬存在问题:
• 远程调⽤时, URL的IP和端⼝号是写死的(http://127.0.0.1:9090/product/), 如果更换IP, 需要修改代
码 。调⽤⽅如何可以不依赖服务提供⽅的IP?
• 多机部署, 如何分摊压⼒?
• 远程调⽤时, URL⾮常容易写错, ⽽且复⽤性不⾼, 如何优雅的实现远程调⽤
• 所有的服务都可以调⽤该接⼝, 是否有⻛险?
654

被折叠的 条评论
为什么被折叠?



