【Spring Cloud】环境和工程基本搭建

系列文章目录

文章目录

一、开发环境安装

1.1、JDK

1.1.1、JDK版本介绍

Oracle从JDK9开始每半年发布⼀个新版本, 新版本发布后, ⽼版本就不再进⾏维护. 但是会有⼏个长期维护的版本.

目前长期维护的版本有: JDK8, JDK11, JDK17, JDK21,在JDK版本的选择上,尽量选择长期维护的版本.

为什么选择JDK17?

Spring Cloud 是基于 SpringBoot 进行开发的, SpringBoot 3.X以下的版本, Spring官方已不再进行维护(还可以继续使用), SpringBoot 3.X的版本, 使用的JDK版本基线为JDK17. 所以本文选择使用JDK17

2、案例介绍

2.1、需求

实现⼀个电商平台(不真实实现, 仅为演示)。一个电商平台包含的内容非常多, 以京东为例, 仅从首页上就可以看到巨多的功能

我们该如何实现呢? 如果把这些功能全部写在⼀个服务⾥, 这个服务将是巨大的.巨多的会员, 巨大的流量, 微服务架构是最好的选择.

微服务应用开发的第⼀步, 就是服务拆分. 拆分后才能进行"各自开发

2.2、服务拆分

服务拆分原则:

微服务到底多小才算"微", 这个在业界并没有明确的标准. 微服务并不是越⼩越好, 服务越⼩, 微服务架构的优点和缺点都会越来越明显.

服务越小, 微服务的独立性就会越来越⾼, 但同时, 微服务的数量也会越多, 管理这些微服务的难度也会提高. 所以服务拆分也要考虑场景.

.

还是以企业管理为例

企业中一个员工的工作内容与企业规模, 项⽬规模等都有关系.

在小公司, ⼀个员工可能需要负责很多部门的事情, 大公司的话, ⼀个部门的工作可能需要多个员工来处理.

拆分微服务---般遵循如下原则:

  1. 单⼀职责原则

    单⼀职责原则原本是面向对象设计中的⼀个基本原则, 它指的是⼀个类应该专注于单⼀功能. 不要存在多于⼀个导致类变更的原因. 在微服务架构中,一个微服务也应该只负责⼀个功能或业务领域, 每个服务应该有清晰的定义和边界, 只关注自己的特定业务领域

  2. 服务自治

    服务自治是指每个微服务都应该具备高度自治的能力, 即每个服务要能做到独立开发, 独立测试, 独立构建, 独立部署, 独立运行.

    以上面的电商系统为例,每⼀个微服务应该有自己的存储, 配置,在进行开发, 构建, 部署, 运行和测试时,并不需要过多关注其他微服务的状态和数据。

  3. 单向依赖

    微服务之间需要做到单向依赖, 严禁循环依赖, 双向依赖

    循环依赖: A -> B -> C ->A

    双向依赖: A -> B, B -> A

微服务架构并无标准架构, 合适的就是最好的, 不然架构师大会也不会各个系统架构百花齐放了.

在架构设计的过程中, 坚持 "合适优于业界领先", 避免"过度设计"(为了设计而设计). 很多业界领先方案并不是⼀群天才在某个时期一下子做出来的, 而是经过数年的发展逐步完善. 业界领先的方案⼤多是"逼"出来的, 随着业务的发展, 量变导致质变,新的问题出现了, 当前的⽅案⽆法满⾜需求, 需要用新的方案来解决. 通过不断的创新和尝试,业界领先的方案才得以形成。

服务拆分示例:

⼀个完整的电商系统是庞大的, 咱们课程中重点关注如何使用SpringCloud解决微服务架构中遇到的问题.

以订单列表为例:订单列表需要包含的主要信息有:

1.订单列表

  1. 商品信息

根据服务的单⼀职责原则, 我们把服务进行拆分为: 订单服务, 商品服务

订单服务: 提供订单ID, 获取订单详细信息

商品服务: 根据商品ID, 返回商品详细信息.

3.数据准备

根据服务自治原则, 每个服务都应有自己独立的数据库

订单服务:

java 复制代码
-- 建库
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)

4.工程搭建

4.1、构建父子工程

4.1.1、创建父工程

1.创建⼀个空的Maven项⽬, 删除所有代码, 只保留pom.xml

2.目录结构:

  1. 完善pom文件

使用properties来进行版本号的统一管理, 使用dependencyManagement来管理依赖, 声明父工程的打包方式为pom

java 复制代码
       <?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>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<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>
</project>

DependencyManagement 和 Dependencies:

1 . dependencies :将所依赖的jar直接加到项目中. 子项目也会继承该依赖.

2 . dependencyManagement :只是声明依赖, 并不实现Jar包引入. 如果子项目需要用到相关依赖,需要显式声明. 如果子项目没有指定具体版本, 会从⽗项⽬中读取version. 如果子项目中指定了版本号,就会使用子项目中指定的jar版本. 此外父⼯程的打包方式应该是pom,不是jar, 这里需要手动使用 packaging 来声明

4.1.2、创建子项目-订单服务

做如下操作:

声明项目依赖 和 项目构建插件:

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

4.1.3、创建子项目-商品服务

创建模块部分同上

下面声明项目依赖和项目构建插件

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

4.2、完善订单服务

4.2.1、完善启动类, 配置文件

启动类:

java 复制代码
 @SpringBootApplication
    public class OrderServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderServiceApplication.class, args);
        }
    }

配置文件:

java 复制代码
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 #配置驼峰⾃动转换

4.2.2 业务代码

  1. 实体类:
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;
}
  1. 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);
        }
    }
  1. Service
java 复制代码
 @Service
    public class OrderService {
        @Autowired
        private OrderMapper orderMapper;
        public OrderInfo selectOrderById(Integer orderId) {
            OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
            return orderInfo;
        }
    }
  1. Mapper
java 复制代码
 @Mapper
 public interface OrderMapper {
        @Select("select * from order_detail where id=#{orderId}")
        OrderInfo selectOrderById(Integer orderId);
    }

完善商品服务类似,故代码不展示。

4.4、远程调用

4.4.1、需求

根据订单查询订单信息时, 根据订单里产品ID, 获取产品的详细信息.

4.4.2、实现

实现思路: : order-service服务向product-service服务发送⼀个http请求, 把得到的返回结果, 和订单结果融合在⼀起, 返回给调用方.

实现方式: 采用Spring 提供的RestTemplate

1 . 定义RestTemplate:

java 复制代码
 @Configuration
    public class BeanConfig {
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
  1. 修改order-service中的 OrderService
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:9090/product/"+
                    orderInfo.getProductId();
            ProductInfo productInfo = restTemplate.getForObject(url,
                    ProductInfo.class);
            orderInfo.setProductInfo(productInfo);
            return orderInfo;
        }
    }

5.RestTemplate

RestTemplate 是从 Spring3.0 开始支持的⼀个 HTTP 请求⼯具, 它是⼀个同步的 REST API 客户端, 提供了常见的REST请求方案的模版

什么是REST?

REST(Representational State Transfer), 表现层资源状态转移.

REST是由HTTP的主要设计者提出来的⼀种软件架构风格.这里面主要有三个概念:

1 . 资源: 网络上的所有事物都可以抽象为资源, 每个资源都有⼀个唯⼀的资源标识符(URI)

2 . 表现层: 资源的表现形式, 比如文本作为资源, 可以用txt格式表现, 也可以通过HTML, XML, JSON等格式来表现, 甚至以二进制的格式表现.

3 . 状态转移: 访问URI, 也就是客⼾端和服务器的交互过程. 客户端用到的手段,只能是HTTP协议. 这个过程中, 可能会涉及到数据状态的变化. 比如对数据的增删改查, 都是状态的转移

REST 是一种设计风格, 指资源在网络中以某种表现形式进行状态转移.

简单来说: REST描述的是在网络中Client和Server的⼀种交互形式, REST本身不实用,实用的是如何设计RESTful API(REST风格的网络接口)

什么是RESTful?

REST 是⼀种设计风格, 并没有⼀个明确的标准. 满足这种设计风格的程序或接口我们称之为RESTful(从单词字面来看就是⼀个形容词). 所以RESTful API 就是满足REST架构风格的接口.

RESTful风格大致有以下几个主要特征:

  1. 资源: 资源可以是⼀个图片, 音频, 视频或者JSON格式等网络上的⼀个实体, 除了⼀些⼆进制的资源外普通的文本资源更多以JSON为载体、面向用户的⼀组数据(通常从数据库中查询而得到)
    2 . 统⼀接口: 对资源的操作. 比如获取, 创建, 修改和删除. 这些操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法. 换言而知,如果使用RESTful风格的接口, 从接⼝上你可能只能定位其资源,但是无法知晓它具体进行了什么操作,需要具体了解其发⽣了什么操作动作要从其HTTP请求方法类型上进行判断
    比如同⼀个的URL:
    GET /blog/{blogId}:查询博客
    DELETE /blog/{blogId}:删除博客
    这些内容都是通过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, 都是为了方便团队开发, 协商以及管理, 不能墨守成规. 尽信规范不如无规范.

6.项目存在问题

• 远程调用时, URL的IP和端口号是写死的(http://127.0.0.1:9090/product/), 如果更换IP, 需要修改代码

◦ 调用方如何可以不依赖服务提供⽅的IP?

• 多机部署, 如何分摊压力?

• 远程调用时, URL非常容易写错, 而且复用性不高, 如何优雅的实现远程调用

• 所有的服务都可以调用该接口, 是否有风险?

除此之外, 微服务架构还面临很多问题, 我会在本系列后续文章一一为大家介绍学习如何使用Spring Cloud 来解决这些问题.

二、总结

以上就是本文全部内容,主要主要搭建了后续Spring Cloud后续组件使用的基本工程环境,以及引出了相关spring cloud可以解决的实际工程问题。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持

相关推荐
狮子座明仔2 小时前
PRL:让大模型推理不再“开盲盒“——过程奖励学习的理论与实践
人工智能·深度学习·学习·机器学习·语言模型
PuppyCoding2 小时前
EasyExcel 导出排除基类字段,不给基类加@ExcelIgnore 的方式。
java·开发语言
发哥来了2 小时前
主流AI视频生成模型商用化能力评测:五大核心维度深度对比
人工智能·音视频
博思云为2 小时前
企业级智能PPT生成:Amazon云+AI驱动,全流程自动化提效
人工智能·语言模型·云原生·数据挖掘·云计算·语音识别·aws
龙山云仓2 小时前
No126:AI中国故事-仓颉:智能的符号编码、知识压缩与文明记忆
大数据·人工智能·深度学习·机器学习·计算机视觉·重构
源代码•宸2 小时前
Golang原理剖析(interface)
服务器·开发语言·后端·golang·interface·type·itab
乾元2 小时前
范式转移:从基于规则的“特征码”到基于统计的“特征向量”
运维·网络·人工智能·网络协议·安全
智算菩萨2 小时前
国内Claude编程完全指南:利用镜像站合法使用Opus、Sonnet与Haiku模型
人工智能·aigc