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

【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.订单列表
2. 商品信息

根据服务的单⼀职责原则, 我们把服务进行拆分为: 订单服务, 商品服务
订单服务: 提供订单ID, 获取订单详细信息
商品服务: 根据商品ID, 返回商品详细信息.

在这里插入图片描述

3.数据准备

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

-- 建库 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 DEFAULT0, `create_time` DATETIME DEFAULTnow(), `update_time` DATETIME DEFAULTnow(),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 DEFAULTnow(), `update_time` DATETIME DEFAULTnow(),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.目录结构:

在这里插入图片描述


2. 完善pom文件
使用properties来进行版本号的统一管理, 使用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 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、创建子项目-订单服务

做如下操作:

在这里插入图片描述


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

<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、创建子项目-商品服务

创建模块部分同上

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

<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、完善启动类, 配置文件

启动类:

@SpringBootApplicationpublicclassOrderServiceApplication{publicstaticvoidmain(String[] args){SpringApplication.run(OrderServiceApplication.class, args);}}

配置文件:

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. 实体类:
@DatapublicclassOrderInfo{privateInteger id;privateInteger userId;privateInteger productId;privateInteger num;privateInteger price;privateInteger deleteFlag;privateDate createTime;privateDate updateTime;}
  1. Controller
@RequestMapping("/order")@RestControllerpublicclassOrderController{@AutowiredprivateOrderService orderService;@RequestMapping("/{orderId}")publicOrderInfogetOrderById(@PathVariable("orderId")Integer orderId){return orderService.selectOrderById(orderId);}}
  1. Service
@ServicepublicclassOrderService{@AutowiredprivateOrderMapper orderMapper;publicOrderInfoselectOrderById(Integer orderId){OrderInfo orderInfo = orderMapper.selectOrderById(orderId);return orderInfo;}}
  1. Mapper
@MapperpublicinterfaceOrderMapper{@Select("select * from order_detail where id=#{orderId}")OrderInfoselectOrderById(Integer orderId);}

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

4.4、远程调用

4.4.1、需求

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

在这里插入图片描述

4.4.2、实现

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

1 . 定义RestTemplate:

@ConfigurationpublicclassBeanConfig{@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}}
  1. 修改order-service中的 OrderService
@ServicepublicclassOrderService{@AutowiredprivateOrderMapper orderMapper;@AutowiredprivateRestTemplate restTemplate;publicOrderInfoselectOrderById(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风格大致有以下几个主要特征:

资源: 资源可以是⼀个图片, 音频, 视频或者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可以解决的实际工程问题。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持

Read more

LeetCode——双指针(初阶)

LeetCode——双指针(初阶)

文章目录 * 简要介绍 * 对撞指针 * 快慢指针 * 相关例题 * 移动零 * 题目描述 * 实现思路 * 版本一 * 版本二 * 最终版 * 复写零 * 题目描述 * 实现思路 * 版本一 * 版本二 简要介绍 我们的双指针算法是算法题中比较常见的一种算法,常见的双指针实际上是有两种的,一种是对撞指针,一种就是我们的快慢指针。 对撞指针 一般用于我们的顺序结构当中,也叫左右指针。 实现思路: 1、对撞指针就是从序列两端向中间移动。 2、终止条件一般就是两个指针相遇了或是错开了。 快慢指针 这个指针又叫龟兔赛跑算法,就是使用两个移动速度不同的指针在序列上移动。常用于我们的环形链表或是数组中。 实现思路: 1、研究问题是不是有循环往复的现象出现。 2、设置一个快指针和一个慢指针,比如让快指针移动两步,慢指针移动一步。 相关例题 移动零 题目描述 给定一个数组 nums,编写一个函数将所有 0 移动到数组

By Ne0inhk
LeetCode算法日记 - Day 5: 长度最小的子数组、无重复字符的最长子串

LeetCode算法日记 - Day 5: 长度最小的子数组、无重复字符的最长子串

目录 1. 长度最小的子数组 1.1 题目解析 1.2 解法 1.3 代码实现 2. 无重复字符的最长子串 2.1 题目解析 2.2 解法 2.3 代码实现 1. 长度最小的子数组 209. 长度最小的子数组 - 力扣(LeetCode) 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。 示例 1: 输入:

By Ne0inhk
初探算法的魅力——【暴力枚举】

初探算法的魅力——【暴力枚举】

点击下面查看作者专栏🔥🔥C语言专栏🔥🔥🌊🌊编程百度🌊🌊🌠🌠如何获取自己的代码仓库🌠🌠 🌐索引与导读 * 暴力枚举(BF)的概念 * 暴力枚举的算法步骤 * 例题讲解 * 经典案例讲解一:百鸡问题 * 题目解析 * 思路方案 * 经典案例讲解二:盛最多水的容器 * 暴力枚举算法 * 最优解 * 经典案例讲解三:两数之和 * 经典案例讲解四:2025 * 💻 代码实现 * 希望读者多多三连 * 给小编一些动力 * 蟹蟹啦! 暴力枚举(BF)的概念 暴力枚举也称为穷举法,是计算机算法中最基础、最直观,但也是最费劲的一种解题思路 像我们平时没有最优解的算法题,往往都可以通过暴力枚举去算出最终结果 * 核心思想 不靠巧妙的技巧,而是利用计算机强大的计算能力,把所有可能的情况列举出来,一个一个去验证,直到找到正确答案 暴力枚举的算法步骤 * 列举 :确定解空间的范围,列出所有可能的解候选者 * 检验 :对每一个候选者进行判断,看它是否满足题目

By Ne0inhk
数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

数据结构:kmp算法,Trie树,以及并查集的干货详解---小白也能看懂

🎬 博主名称:个人主页 🔥 个人专栏: 《算法通关》,《Java讲解》 ⛺️心简单,世界就简单 序言 昨晚数据结构写了一半,做图太累了,文章写的比较慢,这篇应该就是第二篇,后面还有一篇,太困了,真不行了 今天讲一下 kmp算法,Trie, 并查集 目录 序言 KMP算法 原理 next数组 匹配过程 Trie树 并查集 KMP算法 这里说一下kmp的大致情况 用于处理字符串匹配问题,他也是十分的抽象                给一个S[]主串(比较长的那个),P[]为模板串,kmp我们一般用下标1来开始遍历 接下来我们需要去思考的是 1,暴力去怎么做 2,怎么去优化 下面是暴力的模板代码 大概意思就是,每当我们匹配到不一样的部位,我们的P就要从头开始再跟刚刚s的起点+1位置重新匹配,        这样就会造成串的长度很长时候,就会超时,所以我们就要从这个过程中找性质了

By Ne0inhk