Spring Cloud系列—简介

目录

[1 单体架构](#1 单体架构)

[2 集群与分布式](#2 集群与分布式)

[3 微服务架构](#3 微服务架构)

[4 Spring Cloud](#4 Spring Cloud)

[5 Spring Cloud环境和工程搭建](#5 Spring Cloud环境和工程搭建)

[5.1 服务拆分](#5.1 服务拆分)

[5.2 示例](#5.2 示例)

[5.2.1 数据库配置](#5.2.1 数据库配置)

[5.2.2 父子项目创建](#5.2.2 父子项目创建)

[5.2.3 order_service子项目结构配置](#5.2.3 order_service子项目结构配置)

[5.2.4 product_service子项目结构配置](#5.2.4 product_service子项目结构配置)

[5.2.5 服务之间的远程调用](#5.2.5 服务之间的远程调用)

[5.3 RestTemplate](#5.3 RestTemplate)

[5.3.1 REST](#5.3.1 REST)

[5.3.2 RESTful](#5.3.2 RESTful)


1 单体架构

单体架构的系统是指所有功能实现全在一个项目中,该项目部署在一台主机上。但是随着项目规模增大,就会暴露出许多问题:

1.服务器压力变大,负载变高,因为用户数增多,所有的访问均在一个机器上。

2.项目功能之间耦合程度高,业务逻辑复杂,任何一个修改都会导致项目重构、重新打包、部署。

3.任何一个小问题都会导致整个项目崩溃。

要解决这些问题,从两方面考虑:

2 集群与分布式

横向: 增加机器,多个机器分担压力。又称为集群架构

纵向: 项目解耦合,即分割为多个子项目。又称为垂直架构或分布式架构

上述描述也发现集群和分布式存在一些不同和联系:

1.集群是每个机器都做同样的事情;分布式是每个机器都做不同的事情。

2.集群是每个节点功能相同,可以相互替代;分布式是每个节点业务不同,无法相互替代,某个节点宕机,其负责的业务就会无法访问。

3.集群和分布式往往相互配合使用,分布式的子系统可以部署在集群上,因此二者不做特别严格的区分。

3 微服务架构

微服务就是在分布式的基础上,再把子系统拆成更加细粒度、更微小的服务。这里的服务通常只专注做一个功能,因此微服务是经过良好架构设计的分布式架构方案(反之不成立)。

由于微服务是在分布式基础上演化的,因此微服务通常是用来解决分布式架构的一些问题的。

优点:

1.易于开发和维护,每个服务体量小,功能清晰,开发和维护成本低。

2.易于扩展,由于整个系统被分成多个独立的系统,因此扩展其它服务更方便。

3.容错性高,单个服务出现问题,不会影响到整体服务。

4.技术更灵活,不同服务可以采用不同的技术方案,比如不同的语言、框架等等。

缺点:

1.服务之间关系复杂,一个服务的修改需要考虑对其它服务的影响。

2.运营成本高,不同服务需要的运行环境不同,不同服务需要的打包、构建等流程也不同,同时因为集群可用性的保证,使得运营成本更高。

3.服务监控更困难,单体架构只需对整个项目进行监控,而微服务不仅需要对整个服务链路监控,更需要监控每个服务实例。

4.开发和测试更困难,由于不同服务的技术选型不一样,加上服务之间依靠网络通信,因此对于各个服务的联合开发和测试更困难。

5.服务可用性的保证,由于服务数量多,再加上集群环境的复杂,流量高的情况下容易导致某个节点宕机,因此需要使用负载均衡和有效的服务发现来进行流量控制。

4 Spring Cloud

Spring Cloud是分布式微服务的一站式解决方案,并不是Spring官方开发的,而是集成了众多开源好用的组件,并基于SpringBoot风格对组件进行封装方便使用。其主要用来解决如下问题:

1.Distributed/versioned configuration分布式版本配置

2.Service registration and discovery服务注册和发现

3.Routing路由

4.Service-to-service calls服务调用

5.Load balancing负载均衡

6.Circuit Breakers断路器

7.Distributed messaging分布式消息

Spring Cloud使用需要兼容SpringBoot的版本:

在Spring Cloud的规范下,有两个著名的实现:Spring Cloud Netflix和Spring Cloud Alibaba。目前,Spring Cloud Netflix已经逐渐进入维护,因此Spring Cloud Alibaba就变得主流:

|---------|------------------------|----------------------|----------------------|
| | Spring Cloud官方 | Spring Cloud Netflix | Spring Cloud Alibaba |
| 服务注册/发现 | Eureka | Eureka | Nacos |
| 服务调用 | OpenFeign | Feign | Dubbo |
| 配置中心 | SpringCloudConfig | Archaius | Nacos |
| 服务网关 | SpringCloudGateway | Zuul | SpringCloudGateway |
| 负载均衡 | SpringCloudLoadBalance | Ribbon | Dubbo |

5 Spring Cloud 环境和工程搭建

5.1 服务拆分

一个大的服务如何拆分为微服务,需要遵循三个原则:

**1.单一职责原则:**一个微服务专注于单一的功能。

**2.服务自治:**微服务可以独立开发、测试、构建、部署、运行。

**3.单向依赖:**微服务之间要是单向依赖,尽量避免循环依赖、双向依赖(如果必须要,可以使用消息队列MQ)。

5.2 示例

假设此时要实现电商系统的订单服务和商品服务,订单服务提供订单ID,获取订单详细信息;商品服务根据商品ID,返回商品详细信息。

5.2.1 数据库配置

以下是相关数据库、表创建和初始化代码:

sql 复制代码
-- 建库

create database if not exists cloud_order charset utf8mb4;

use cloud_order;

-- 订单表

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;

use cloud_product;

-- 产品表

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);

5.2.2 父子项目创建

采用父子项目创建方式创建Spring Cloud项目,先创建空的Maven项目springcloud_order,再在该项目上创建两个子项目module:

父项目pom文件:

XML 复制代码
<?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>springcloud_order</artifactId>

    <version>1.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <modules>

        <module>order_service</module>

        <module>product_service</module>

    </modules>



    <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>18</maven.compiler.source>

        <maven.compiler.target>18</maven.compiler.target>

        <java.version>18</java.version>

        <mybatis.version>3.0.3</mybatis.version>

        <mysql.version>8.0.31</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>

</project>

注意:父项目的打包方式要修改为pom,即<packaging>pom</packaging>。

上述pom文件用到了和依赖dependency相关的两个标签<dependencyManagement>和<dependencies>,二者区别如下:

****<dependencies>:****直接将依赖引入项目中,如果是父项目的pom文件,则引入的依赖子项目也会继承。

****<dependencyManagement>:****声明依赖,并没有引入依赖到项目中。如果子项目需要用到声明的依赖,需要显示引入。如果引入依赖时没有声明版本号,默认使用父项目声明依赖的版本号。如果引入依赖指定了具体版本,则就使用子项目版本号的依赖。

使用SpringBoot开发时之所以没有使用<dependencyManagement>进行依赖的版本管理,就是因为SpringBoot框架的pom文件已经做了默认的版本控制了。

5.2.3 order_service子项目结构配置

由于创建的都是空项目,因此需要手动配置启动类、pom文件、SpringBoot配置文件(yml)等内容:

启动类:

java 复制代码
@SpringBootApplication

public class OrderServiceApplication {

    public static void main(String[] args) {

        SpringApplication.run(OrderServiceApplication.class, args);

    }

}

pom文件:

XML 复制代码
    <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>

SpringBoot配置文件:

bash 复制代码
server:

  port: 8080

spring:

  datasource:

    url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false

    username: root

    password: root

    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:

  configuration: # 配置打印 MyBatis日志

    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

    map-underscore-to-camel-case: true #配置驼峰自动转换

model层:

java 复制代码
@Data

public class OrderInfo {

    private Integer id;

    private Integer userId;

    private Integer productId;

    private Integer num;

    private Integer price;

    private Integer deleteFlag;

    private Date createTime;

    private Date updateTime;

}

service层:

java 复制代码
@Service

public class OrderService {

    @Autowired

    private OrderMapper orderMapper;



    public OrderInfo selectOrderById(Integer orderId) {

        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);

        return orderInfo;

    }

}

mapper层:

java 复制代码
@Mapper

public interface OrderMapper {

    @Select("select * from order_detail where id=#{orderId}")

    OrderInfo selectOrderById(Integer orderId);

}

controller层:

java 复制代码
@RequestMapping("/order")

@RestController

public class OrderController {

    @Autowired

    private OrderService orderService;



    @RequestMapping("/{orderId}")

    public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {

        return orderService.selectOrderById(orderId);

    }

}

运行代码结果如下:

5.2.4 product_service子项目结构配置

由于创建的都是空项目,因此需要手动配置启动类、pom文件、SpringBoot配置文件(yml)等内容:

启动类:

java 复制代码
@SpringBootApplication

public class ProductServiceApplication {

    public static void main(String[] args) {

        SpringApplication.run(ProductServiceApplication.class, args);

    }

}

pom文件:

XML 复制代码
    <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>

SpringBoot配置文件:

bash 复制代码
server:

  port: 8081

spring:

  datasource:

    url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false

    username: root

    password: root

    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:

  configuration: # 配置打印 MyBatis日志

    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

    map-underscore-to-camel-case: true #配置驼峰自动转换

model层:

java 复制代码
@Data

public class OrderInfo {

    private Integer id;

    private Integer userId;

    private Integer productId;

    private Integer num;

    private Integer price;

    private Integer deleteFlag;

    private Date createTime;

    private Date updateTime;

}

service层:

java 复制代码
@Service

public class OrderService {

    @Autowired

    private OrderMapper orderMapper;



    public OrderInfo selectOrderById(Integer orderId) {

        OrderInfo orderInfo = orderMapper.selectOrderById(orderId);

        return orderInfo;

    }

}

mapper层:

java 复制代码
@Mapper

public interface OrderMapper {

    @Select("select * from order_detail where id=#{orderId}")

    OrderInfo selectOrderById(Integer orderId);

}

controller层:

java 复制代码
@RequestMapping("/order")

@RestController

public class OrderController {

    @Autowired

    private OrderService orderService;



    @RequestMapping("/{orderId}")

    public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {

        return orderService.selectOrderById(orderId);

    }

}

运行代码结果如下:

5.2.5 服务之间的远程调用

现在两个服务可以各自独立运行,但是订单信息上是需要显示商品信息的,因此订单服务需要访问商品服务根据订单上的商品id查询订单对应的商品。

首先在order_service项目中引入商品信息类,同时在订单信息类添加该属性:

java 复制代码
@Data

public class OrderInfo {

    private Integer id;

    private Integer userId;

    private Integer productId;

    private Integer num;

    private Integer price;

    private Integer deleteFlag;

    private Date createTime;

    private Date updateTime;

    private ProductInfo productInfo;

}

这里使用RestTemplate来实现HTTP服务:

java 复制代码
@Configuration

public class BeanConfig {

    @Bean

    public RestTemplate restTemplate() {

        return new RestTemplate();

    }

}

修改业务层代码:

java 复制代码
@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对象的getForObject()方法来远程调用product_service服务的接口,返回类型是ProductInfo,把返回的对象设置到orderInfo的属性中,即可实现远程调用并返回订单信息和详细商品信息。

{"id":1,"userId":2001,"productId":1001,"num":1,"price":99,"deleteFlag":0,"createTime":"2025-07-14T07:44:48.000+00:00","updateTime":"2025-07-14T07:44:48.000+00:00","productInfo":{"id":1001,"productName":"T恤","productPrice":101,"state":0,"createTime":"2025-07-14T07:44:48.000+00:00","updateTime":"2025-07-14T07:44:48.000+00:00"}}

5.3 RestTemplate

5.3.1 REST

REST全称是Representational State Transfer(表现层资源状态转移),是Roy Fielding在2000年提出的一种软件架构风格,指资源在网络中以某种表现形式进行状态转移。

**资源:**网络中所有事物都可以称为资源,每个资源都有唯一的资源标识符(URL)。

**表现层:**资源的表现形式,比如文本txt、图片png、甚至JSON、HTML等等都是表现形式。

**状态转移:**客户端通过访问URL,引起资源在网络传输中的状态变化(比如增删改查)。

5.3.2 RESTful

REST只是一种架构,并没有具体的实现。而RESTful就是具有REST架构的设计风格,接口可以满足这种风格。

RESTful基于HTTP协议,定义了两种特征:资源和统一接口。资源可以是图片、文本、二进制等等,统一接口是对资源的操作(增删改查),对应HTTP协议的POST、DELETE、PUT、GET方法。

RestTemplate就是Spring对HTTP封装并使用RESTful风格的对象,内部实现了连接的自动开启和关闭,调用者只需提供URL和参数即可。

缺点:

1.操作繁琐且请求方法不可见:实现RESTful风格的接口通常需要根据请求方法区分操作,但是URL并不能直接显示请求方法,需要抓包工具才能得知。并且一些浏览器不支持其它请求,需要额外处理。

2.过分强调资源,简单的增删改查请求并不能满足复杂的业务需求,增加开发难度。

下篇文章:

Spring Cloud系列---Eureka服务注册/发现https://blog.csdn.net/sniper_fandc/article/details/149937589?fromshare=blogdetail&sharetype=blogdetail&sharerId=149937589&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link

相关推荐
右手嘚温暖2 小时前
分布式事务Seata、LCN的原理深度剖析
spring boot·分布式·后端·spring·spring cloud·中间件·架构
麦兜*4 小时前
Spring Boot集成方案 + Elasticsearch向量检索,语义搜索核弹
java·spring boot·python·spring·elasticsearch·spring cloud·系统架构
黑暗也有阳光15 小时前
@FeignClient 中 fallbackFactory 与 fallback 的区别详解
spring boot·spring·spring cloud
黑暗也有阳光15 小时前
Sentinel 与 Hystrix 熔断降级机制对比解析
分布式·spring cloud·微服务
JavaArchJourney18 小时前
Spring Cloud Hystrix 核心原理
后端·spring cloud
林林code1 天前
从源码的角度解读 Nacos 是如何动态刷新配置的
spring cloud
太阳之神aboluo1 天前
SpringCloud (4) 分布式事务
java·spring·spring cloud
林林code2 天前
Springboot中的yml为单个的服务配置Feign不生效.md
spring cloud
麦兜*2 天前
Spring Boot 与 Ollama 集成部署私有LLM服务 的完整避坑指南,涵盖 环境配置、模型管理、性能优化 和 安全加固
java·spring boot·后端·安全·spring cloud·性能优化