【SpringCloud】Spring Cloud 开发环境搭建与基础工程构建

news2024/9/22 7:33:35

目录

  • 环境和工程搭建
    • 开发环境安装
      • JDK
        • JDK版本介绍
        • JDK17安装
          • Windows
          • Linux - Ubuntu
          • Linux - CentOs
      • MySQL安装
        • Ubuntu
        • CentOS
    • 案例介绍
      • 需求
      • 服务拆分
        • 服务拆分原则
        • 服务拆分示例
    • 数据准备
    • 工程搭建
      • 构建父子工程
        • 创建父工程
          • DependencyManagement 和 Dependencies
          • Spring Cloud版本
        • 创建子项目-订单服务
        • 创建子项目-商品服务
      • 完善订单服务
        • 完善启动类, 配置文件
        • 业务代码
        • 测试
      • 完善商品服务
        • 完善启动类, 配置文件
        • 业务代码
        • 测试
      • 远程调用
        • 需求
        • 实现
        • 测试
    • RestTemplate
      • 什么是 REST?
      • 什么是 RESTful?
      • RESTful 实践
    • 项目存在问题


环境和工程搭建

Spring Cloud 是分布式微服务架构的⼀站式解决⽅案, 也可以理解为Spring Cloud是帮助我们管理这些微服务的, 那么在学习Spring Cloud之前, 我们需要先具备⼀些微服务.

开发环境安装

JDK

JDK版本介绍

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

⽬前⻓期维护的版本有: JDK8, JDK11, JDK17, JDK21

在 JDK版本的选择上,尽量选择⻓期维护的版本.

为什么选择JDK17?

Spring Cloud 是基于 SpringBoot 进⾏开发的, SpringBoot 3.X以下的版本, Spring官⽅已不再进⾏维护(还可以继续使⽤), SpringBoot 3.X的版本, 使⽤的JDK版本基线为JDK17. 鉴于JDK21 是2023.09⽉发布的, 很多功能还没有在⽣产环境验证, 所以选择使⽤JDK17来学习

JDK17安装
Windows
  1. 下载JDK

下载地址: https://www.oracle.com/java/technologies/downloads/#java17

Windows环境下载:

  1. 安装JDK

双击运⾏exe⽂件, 选择安装⽬录(D:\Program Files\Java\jdk-17\), 直⾄安装完成.

  1. 配置环境变量

配置JAVA_HOME

变量名变量值
JAVA_HOMED:\Program Files\Java\jdk-17

在Path中添加 JAVA_HOME 的值

在系统环境变量中找到Path,点击编辑->新建,加上变量值 %JAVA_HOME%\bin

这里最好也区分一下 JDK1.8 和 JDK17

验证

在 cmd 中输⼊ java -version , 确认JDK是否安装和配置成功

Linux - Ubuntu

这里用的是 Ubuntu20.4

  1. 安装JDK
更新软件包
sudo apt update 
安装JDK
sudo apt install openjdk-17-jdk

Linux上安装的JDK为OpenJDK

OpenJDK是Oracle JDK的开放源码版本, Oracle公司每次发布JDK新版本时, 会发布两个版本: ⼀个是

Oracle JDK(加了⼀些Oracle公司商业技术) , 另⼀个是OpenJDK(纯净版).

OpenJDK在功能上, ⼏乎可以认为⼀模⼀样, 学习阶段使⽤OpenJDK就可以.

  1. 确定JDK版本

输⼊ java -version , 确认JDK版本

检查安装的是哪个OpenJDK
# dpkg --list | grep -i jdk
移除 openjdk包:
# apt-get purge openjdk*
卸载 OpenJDK 相关包:
# apt-get purge icedtea-* openjdk-*
检查所有 OpenJDK包是否都已卸载完毕:
# dpkg --list | grep -i jdk
Linux - CentOs
  1. 下载JDK
wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm

获取链接⽅式

https://www.oracle.com/java/technologies/downloads/#java17

  1. 安装JDK
yum -y install jdk-17_linux-x64_bin.rpm
  1. 验证JDK
[root@VM-0-17-centos spring-cloud]# java -version
java version "17.0.10" 2024-01-16 LTS
Java(TM) SE Runtime Environment (build 17.0.10+11-LTS-240)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.10+11-LTS-240, mixed mode, sharing)

MySQL安装

Ubuntu
  1. 使⽤apt安装MySQL
#查找安装包
apt list |grep "mysql-server"

#安装mysql
sudo apt install mysql-server

执行结果

root@bite:~# apt list | grep "mysql-server"  # 查找 mysql 的安装包

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

default-mysql-server-core/focal,focal 1.0.5ubuntu2 all
default-mysql-server/focal,focal 1.0.5ubuntu2 all
mysql-server-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 amd64
mysql-server-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 i386
mysql-server-core-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 amd64
mysql-server-core-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 i386
mysql-server/focal-updates,focal-updates,focal-security,focal-security 8.0.35-0ubuntu0.20.04.1 all

ubuntu@VM-24-3-ubuntu:~$ sudo apt install mysql-server  # 输入安装命令

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
...
After this operation, 243 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y  # 输入 Y 确认

emitting matrix : 100% |###########################################| 

done!
update-alternatives: using /var/lib/mecab/dic/ipadic-utf8 to provide /var/lib/mecab/dic/debian (mecab-dictionary) in auto mode
Setting up libhtml-parser-perl:amd64 (3.76-1build2) ...
Setting up libhttp-message-perl (6.36-1) ...
Setting up mysql-server (8.0.35-0ubuntu0.22.04.1) ...
Setting up libcgi-pm-perl (4.54-1) ...
Setting up libhtml-template-perl (2.97-1.1) ...
Setting up libcgi-fast-perl (1:2.15-1) ...
Processing triggers for man-db (2.10.2-1) ...
Processing triggers for libc-bin (2.35-0ubuntu3.4) ...
  1. 查看MySQL状态
sudo systemctl status mysql

执行结果

root@bite:~# apt list | grep "mysql-server"  # 查找 mysql 的安装包

WARNING: apt does not have a stable CLI interface. Use with caution in scripts.

default-mysql-server-core/focal,focal 1.0.5ubuntu2 all
default-mysql-server/focal,focal 1.0.5ubuntu2 all
mysql-server-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 amd64
mysql-server-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 i386
mysql-server-core-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 amd64
mysql-server-core-8.0/focal-updates,focal-security 8.0.35-0ubuntu0.20.04.1 i386
mysql-server/focal-updates,focal-updates,focal-security,focal-security 8.0.35-0ubuntu0.20.04.1 all

ubuntu@VM-24-3-ubuntu:~$ sudo apt install mysql-server  # 输入安装命令

Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
...
After this operation, 243 MB of additional disk space will be used.
Do you want to continue? [Y/n] Y  # 输入 Y 确认

emitting matrix : 100% |###########################################| 

done!
update-alternatives: using /var/lib/mecab/dic/ipadic-utf8 to provide /var/lib/mecab/dic/debian (mecab-dictionary) in auto mode
Setting up libhtml-parser-perl:amd64 (3.76-1build2) ...
Setting up libhttp-message-perl (6.36-1) ...
Setting up mysql-server (8.0.35-0ubuntu0.22.04.1) ...
Setting up libcgi-pm-perl (4.54-1) ...
Setting up libhtml-template-perl (2.97-1.1) ...
Setting up libcgi-fast-perl (1:2.15-1) ...
Processing triggers for man-db (2.10.2-1) ...
Processing triggers for libc-bin (2.35-0ubuntu3.4) ...
  1. MySQL 安装安全设置

默认的 MySQL 设置是不安全的, MySQL 安装提供了⼀个安全脚本, ⽤于解决不太安全的默认选项.

运⾏以下命令:

sudo mysql_secure_installation

接下来按照提⽰输⼊:

root@hcss-ecs-0bb1:~# sudo mysql_secure_installation #安装安全设置

Securing the MySQL server deployment.

Connecting to MySQL using a blank password.

VALIDATE PASSWORD COMPONENT can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD component?

Press y|Y for Yes, any other key for No: Y #是否设置验证密码组件

There are three levels of password validation policy:

LOW    Length >= 8
MEDIUM Length >= 8, numeric, mixed case, and special characters
STRONG Length >= 8, numeric, mixed case, special characters and dictionary file

Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2 #设置密码强度

Skipping password set for root as authentication with auth_socket is used by default.
If you would like to use password authentication instead, this can be done with the "ALTER_USER" command.
See https://dev.mysql.com/doc/refman/8.0/en/alter-user.html#alter-user-password-management for more information.

By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y #默认情况下,MySQL安装有一个匿名用户, 允许任何人登录MySQL. 是否删除匿名用户?
Success.

Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y #仅应允许root从'localhost'连接
Success.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production environment.

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y #默认情况下, MySQL带有一个test数据库, 是否删除?
- Dropping test database...
Success.

- Removing privileges on test database...
Success.

Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y #是否现在加载配置, 使刚才的修改生效? 
Success.

All done!
  1. 设置密码

连接mysql服务器

sudo mysql

使⽤alter user 命令修改密码

--密码强度为2时, 密码要包含⼤⼩写字符, 特殊符号
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'BITE@yyds.666';

卸载MySQL

# 停止 MySQL
sudo systemctl stop mysql

# 卸载 MySQL
sudo apt-get remove --purge mysql-server mysql-client mysql-common

# 删除 MySQL 配置文件和数据
sudo rm -rf /etc/mysql /var/lib/mysql

# 清理残留文件和目录
sudo apt-get autoremove
sudo apt-get autoclean

# 验证卸载结果
mysql --version
CentOS
  1. 确认当前的系统版本

不同的系统版本对应的MySQL安装版本不同, 需要先确认系统版本

[root@VM-0-17-centos ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
  1. 下载MySQL源
[root@VM-0-17-centos ~]# wget https://dev.mysql.com/get/mysql80-communityrelease-el7-11.noarch.rpm

MySQL源地址查找⽅法:

  1. 访问MySQL开发者专区:https://dev.mysql.com/downloads/repo/yum

  1. 点击Download,右键复制链接

  1. 安装MySQL
# 安装发布包
yum -y install mysql80-community-release-el7-11.noarch.rpm 

# 安装 MySQL,过程中输入 Y 确认
yum install mysql-community-server
  1. 启动mysql
systemctl start mysqld
  1. 查看MySQL状态
● mysqld.service - MySQL Server
   Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled)
   Active: active (running) since 一 2024-01-29 18:43:59 CST; 30s ago
     Docs: man:mysqld(8)
           http://dev.mysql.com/doc/refman/en/using-systemd.html
  Process: 2464 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
 Main PID: 2667 (mysqld)
   Status: "Server is operational"
    CGroup: /system.slice/mysqld.service
            └─2667 /usr/sbin/mysqld

129 18:43:53 VM-0-17-centos systemd[1]: Starting MySQL Server...
129 18:43:59 VM-0-17-centos systemd[1]: Started MySQL Server.
  1. 设置开机⾃启动
systemctl enable mysqld
  1. 查看mysql密码, 并登录

⾸次安装成功,MySQL把root⽤⼾的默认密码保存在错误⽇志中

[root@VM-0-17-centos ~]# grep "password" /var/log/mysqld.log
2024-01-29T10:43:55.523053Z 6 [Note] [MY-010454] [Server] A temporary password 
is generated for root@localhost: &hpkaqO:c4oK #默认⽣成的密码
[root@VM-0-17-centos ~]# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.36

Copyright (c) 2000, 2024, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
  1. 修改密码
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'BITE@yyds.666';
Query OK, 0 rows affected (0.01 sec)

默认安装模式, MySQL对密码强度有⼀定要求, 不能设置的过于简单, 否则会报ERROR

默认要求密码⻓度⼤于8, 且包含⼤写字⺟, ⼩写字⺟, 数字及特殊字符

案例介绍

需求

实现⼀个电商平台(不真实实现, 仅为演⽰)

⼀个电商平台包含的内容⾮常多, 以京东为例, 仅从⾸⻚上就可以看到巨多的功能

我们该如何实现呢? 如果把这些功能全部写在⼀个服务⾥, 这个服务将是巨⼤的.

巨多的会员, 巨⼤的流量, 微服务架构是最好的选择.

微服务应⽤开发的第⼀步, 就是服务拆分. 拆分后才能进⾏"各⾃开发"

服务拆分

服务拆分原则

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

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

还是以企业管理为例

企业中一个员工的工作内容与企业规模、项目规模等都有关系。

在小公司,一个员工可能需要负责很多部门的事情;而在大公司,一个部门的工作可能需要多个员工来处理。

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

  1. 单一职责原则

单一职责原则原本是面向对象设计中的一个基本原则,它指的是一个类应该专注于单一功能,不要存在多于一个导致类变更的原因。

在微服务架构中,一个微服务也应该只负责一个功能或业务领域,每个服务应该有清晰的定义和边界,只关注自己的特定业务领域。

组织团队也是,一个人专注做一件事情的效率远高于同时关注多件事情。

比如一个人同时管理和维护一份代码,要比多个人同时维护多份代码的效率高。

例如电商系统:

电商系统

  1. 服务自治

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

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

例如企业管理

每个部门负责自己的事情,并且尽可能少的受其他团队影响。

研发部门只负责需求功能的开发,而不负责需求文档的书写和 UI 的设计。并且其他部门的人员变动、流程变更,也尽可能少的影响研发部门。部门和部门之间尽可能自治。

服务自治

  1. 单向依赖

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

  • 循环依赖:A -> B -> C -> A
  • 双向依赖:A -> B, B -> A

单向依赖

如果一些场景确实无法避免循环依赖或者双向依赖,可以考虑使用消息队列等其他方式来实现。

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

在架构设计的过程中,坚持"合适优于业界领先",避免"过度设计"(为了设计而设计)。

罗马不是一天建成的,很多业界领先方案并不是一群天才在某个时期一下子做出来的,而是经过数年的发展逐步完善。业界领先的方案大多是"逼"出来的,随着业务的发展,量变导致质变,新的问题出现了,当前的方案无法满足需求,需要用新的方案来解决。通过不断的创新和尝试,业界领先的方案才得以形成。

服务拆分示例

⼀个完整的电商系统是庞⼤的, 当然这也不是我们的重点, 我们重点关注如何使⽤Spring Cloud解决微服务架构中遇到的问题.

以订单列表为例:

简单来看, 这个⻚⾯提供了以下信息:

  1. 订单列表
  2. 商品信息

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

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

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

真实的订单服务和我们的并不⼀致, 企业中拆分的粒度更加细, 此处仅为举例

数据准备

根据服务⾃治原则, 每个服务都应有⾃⼰独⽴的数据库

订单服务:

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

-- 创建 cloud_product 数据库
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);

工程搭建

构建父子工程

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

⽬录结构:

创建好以后删除 src 目录

  1. 完善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>

主要是复制这一部分

<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 可能有以下部分,但我们复制的上面这部分已经有了,因此要把原来的下面这部分删除掉

<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>

最终是这样的(注意要多加 <packaging>pom</packaging>

<?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.hsu</groupId>
    <artifactId>spring-cloud-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>

</project>

DependencyManagement 和 Dependencies
  1. dependencies:将所依赖的 jar 直接加到项目中,子项目也会继承该依赖。
  2. dependencyManagement:只是声明依赖,并不实现 Jar 包引入。如果子项目需要用到相关依赖,需要显式声明。如果子项目没有指定具体版本,会从父项目中读取 version。如果子项目中指定了版本号,就会使用子项目中指定的 jar 版本。此外父工程的打包方式应该是 pom,而不是 jar,这里需要手动使用 packaging 来声明。

Spring Boot 实现依赖 jar 包版本的管理,也是这种方式

依赖管理

依赖 Jar 的版本判断

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

上述代码中,lombok 会被直接引入到当前项目以及子项目中,mysql-connector-j 不会实际引入 jar,子项目继承时必须显式声明。

Spring Cloud版本

Spring Cloud 是基于SpringBoot搭建的, 所以Spring Cloud 版本与SpringBoot版本有关

我们项⽬中使⽤的SpringBoot 版本为 3.1.6, 对应的Spring Cloud版本应该为2022.0.x, 选择任⼀就可以

创建子项目-订单服务

声明项⽬依赖和项⽬构建插件

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

声明项⽬依赖和项⽬构建插件

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

完善订单服务

完善启动类, 配置文件

启动类

@SpringBootApplication
public class OrderServiceApplication {
    public static void main(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: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true # 配置驼峰自动转换
业务代码
  1. 实体类
@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
@RestController
@RequestMapping("/order")
public class OrderController {
    
    @Autowired
    private OrderService orderService;

    @RequestMapping("/{orderId}")
    public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {
        return orderService.selectOrderById(orderId);
    }
}
  1. Service
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;

    public OrderInfo selectOrderById(Integer orderId) {
        return orderMapper.selectOrderById(orderId);
    }
}
  1. Mapper
@Mapper
public interface OrderMapper {
    
    @Select("SELECT * FROM order_detail WHERE id = #{orderId}")
    OrderInfo selectOrderById(Integer orderId);
}
测试

访问url: http://127.0.0.1:8080/order/1

⻚⾯正常返回结果:

完善商品服务

完善启动类, 配置文件

启动类

@SpringBootApplication
public class ProductServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductServiceApplication.class, args);
    }
}

配置⽂件

server:
  port: 9090

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

后⾯需要多个服务⼀起启动, 所以设置为不同的端⼝号

业务代码
  1. 实体类
@Data
public class ProductInfo {
    private Integer id;
    private String productName;
    private Integer productPrice;
    private Integer state;
    private Date createTime;
    private Date updateTime;
}
  1. Controller
@RestController
@RequestMapping("/product")
public class ProductController {
    
    @Autowired
    private ProductService productService;

    @RequestMapping("/{productId}")
    public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
        System.out.println("收到请求, Id:" + productId);
        return productService.selectProductById(productId);
    }
}
  1. Service
@Service
public class ProductService {
    
    @Autowired
    private ProductMapper productMapper;

    public ProductInfo selectProductById(Integer id) {
        return productMapper.selectProductById(id);
    }
}
  1. Mapper
@Mapper
public interface ProductMapper {
    
    @Select("SELECT * FROM product_detail WHERE id = #{id}")
    ProductInfo selectProductById(Integer id);
}
测试

访问url: http://127.0.0.1:9090/product/1001

⻚⾯正常返回结果:

远程调用

需求

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

这就需要 http 访问,从商品服务获取订单服务的信息

实现

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

要先修改订单服务的实体类,先把 ProductInfo 复制过来,再修改 OrderInfo,添加下面这个字段

private ProductInfo productInfo;

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

实现http请求的⽅式, 有很多, 可参考: https://zhuanlan.zhihu.com/p/670101467

  1. config 包:定义RestTemplate
@Configuration
public class BeanConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
  1. 修改order-service中的 OrderService
@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

⻚⾯返回结果:

RestTemplate

RestTemplate 是从 Spring 3.0 开始支持的一个 HTTP 请求工具,它是一个同步的 REST API 客户端,提供了常见的 REST 请求方案的模板。

什么是 REST?

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

REST 是由 HTTP 的主要设计者 Roy Fielding 博士在 2000 年他的博士论文中提出来的一种软件架构风格。这里面主要有三个概念:

  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,都是为了方便团队开发、协商以及管理,不能墨守成规。尽信书不如无书,尽信规范不如无规范。

项目存在问题

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

    • 调⽤⽅如何可以不依赖服务提供⽅的IP?
  • 多机部署, 如何处理, 如何分摊压⼒?

  • 返回结果如何共用?

  • 远程调⽤时, URL⾮常容易写错, ⽽且复⽤性不⾼, 如何优雅的实现远程调⽤

  • 所有的服务都可以调⽤该接⼝, 是否有⻛险?

除此之外, 微服务架构还⾯临很多问题, 接下来我们学习如何使⽤Spring Cloud 来解决这些问题.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2139405.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【生日视频制作】奔驰梅赛德斯大奔提车交车仪式感视频拍照AE模板修改文字软件一键生成器教程特效素材【AE模板】

生日视频制作教程奔驰梅赛德斯大奔提车交车仪式感视频拍照AE模板修改文字特效广告生成神器素材祝福玩法AE模板工程 AE模板套用改图文教程↓↓&#xff1a; 怎么如何做的【生日视频制作】奔驰梅赛德斯大奔提车交车仪式感视频拍照AE模板修改文字软件一键生成器教程特效素材【AE模…

下一代 AI 搜索:多智能体 + 系统2,解决 AI 搜索在复杂信息性能下降问题

下一代 AI 搜索&#xff1a;多智能体 系统2&#xff0c;解决 AI 搜索在复杂信息性能下降问题 AI 搜索&#xff1a;从搜索引擎到答案引擎① AI 搜索市场现状&#xff08;可跳过&#xff09;② 巨好用的 AI 工具推荐程序员的垂直搜索引擎 devv.ai ③ 多智能体 系统2&#xff0c…

易扫功能介绍

背景 之前开发扫描工具&#xff0c;在大家使用过程中提出了很多改进建议&#xff0c;其中最多的就是&#xff0c;IP地址变动&#xff0c;导致无法扫描。易扫软件系统解决了这个问题&#xff0c;同时易扫服务端&#xff0c;支持多操作系统平台安装。 系统架构 主要功能介绍 支…

字典+泛型的栈与队列+委托

字典 在System.Collections.Generic下&#xff0c;对应HashTable,添加了泛型的特性&#xff0c;性能更高更安全&#xff0c;在内存中散列排布&#xff0c;存储也是键值对。 Dictionary<键的数据类型&#xff0c;值的数据类型> 字典名new Dictionary<键的数据类型&am…

18063 圈中的游戏

### 思路 1. 创建一个循环链表表示围成一圈的 n 个人。 2. 从第一个人开始报数&#xff0c;每报到 3 的人退出圈子。 3. 重复上述过程&#xff0c;直到只剩下一个人。 4. 输出最后留下的人的编号。 ### 伪代码 1. 创建一个循环链表&#xff0c;节点表示每个人的编号。 2. 初始…

【视觉中国-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

TCP Analysis Flags 之 TCP ZeroWindow

前言 默认情况下&#xff0c;Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态&#xff0c;并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时&#xff0c;会对每个 TCP 数据包进行一次分析&#xff0c;数据包按照它们在数据包列表中出现的顺序进行处理。可…

9. 什么是 Beam Search?深入理解模型生成策略

是不是总感觉很熟悉&#xff1f; 在之前第5&#xff0c;7&#xff0c;8篇文章中&#xff0c;我们都曾经用到过与它相关的参数&#xff0c;而对于早就有着实操经验的同学们&#xff0c;想必见到的更多。这篇文章将从示例到数学原理和代码带你进行理解。 Beam Search 对应的中文翻…

工厂模式(一):简单工厂模式

一、概念 顾名思义&#xff0c;带着工厂&#xff0c;两字肯定就是有标准、快速、统一等等一些工厂独有的特点。 那么什么是简单工厂模式呢&#xff1f; 定义&#xff1a;简单工厂模式是一种创建对象的设计模式&#xff0c;它定义了一个工厂类通过某个静态方法来生成不同类型的…

基于AutoDL部署langchain-chatchat-0.3.1实战

一、租用AutoDL云服务器&#xff0c;配置环境 1.1 配置AutoDL环境 注册好autodl账户之后&#xff0c;开始在上面租服务器&#xff0c;GPU我选择的是RTX4090*2&#xff0c;西北B区&#xff0c;基础镜像选择的是Pytorch-2.3.0-python-3.12&#xff08;ubuntu22.04&#xff09;-…

夸克网盘电脑端和手机端如何查看自己分享的文件

夸克网盘有些地方做的还是有点抽象&#xff0c;好多东西是真的找不到。 找了半天终于找到了自己分享的文件&#xff0c;给大家分享下。 电脑端 点击左侧栏的“快传”&#xff0c;然后点击“我分享的” 手机端 手机端也是类似&#xff0c;点击“快传”后再点击“我分享的”&a…

白月光git

感觉bug好多干脆直接从头到脚梳理 感冒不嘻嘻 近况是&#xff1a; 早起学习 开车去沟里 把蜜蜂拍到狗身上 把车开回来 吃席 安装git和VScode 都是从官网上装的&#xff0c;不说那么多咯&#xff0c;之前说过&#xff1a; 进程间也要唠一唠-CSDN博客https://blog.csdn.net…

Spring4-IoC3-手写IoC

Spring框架的IoC是基于Java反射机制实现的 Java反射机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff0c;这种动态获取信息以及动态调用对象方法的功…

【AI学习笔记】初学机器学习西瓜书的知识点概要记录

初学机器学习西瓜书的知识点概要记录 1.1 机器学习1.2 典型的机器学习过程1.2 机器学习理论1.3 基本术语1.4 归纳偏好1.5 NFL定理2.1 泛化能力2.2 过拟合和欠拟合2.3 三大问题2.4 评估方法2.5 调参与验证集2.6 性能度量2.7 比较检验 以下内容出自周志华老师亲讲西瓜书 1.1 机器…

复习:数组

目录 数组名 一般性理解 下标引用与间接访问 例外 一维数组 声明与初始化 下标引用 内存分配 长度计算 二维数组 内存分配 长度计算 声明与初始化 数组指针 引入 数组指针 一级指针 引入 一级指针 章尾问题 数组名 一般性理解 数组名是一个指向&#x…

DockerLinux安装DockerDocker基础

Linux软件安装 yum命令安装 通过yum命令安装软件,是直接把软件安装到Linux系统中 安装和卸载都比较麻烦,因为软件和系统是强关联的 Docker docker是一种容器技术,可以解决软件和系统强关联关系,使得软件的安装和卸载更方便,它可以将我们的应用以及依赖进行打包,制作出一个镜…

算法:TopK问题

题目 有10亿个数字&#xff0c;需要找出其中的前k大个数字。 为了方便讲解&#xff0c;这里令k为5。 思路分析&#xff08;以找前k大个数字为例&#xff09; 很容易想到&#xff0c;进行排序&#xff0c;然后取前k个数字即可。 但是&#xff0c;难点在于&#xff0c;10亿个数…

人工智能GPT____豆包使用的一些初步探索步骤 体验不一样的工作

豆包工具是我使用比较频繁的一款软件&#xff0c;其集合了很多功能。对话 图像 AI搜索 伴读等等使用都非常不错。电脑端安装集合了很多功能。 官网直达&#xff1a;豆包 使用我的文案创作能力&#xff0c;您可以注意以下几个技巧&#xff1a; 明确需求&#xff1a; 尽可能具…

Linux进阶 把用户加入和移除用户组

1、Linux 单用户多任务,多用户多任务概念 Linux 是一个多用户、多任务的操作系统。 单用户多任务、多用户多任务 概念; Linux 的 单用户、多任务以 beinan 登录系统,进入系统后,我要打开gedit 来写文档,但在写文档的过程中,我感觉少点音乐,所以又打开xmms 来点音乐;当…

C语言:联合和枚举

一. 联合体 1.联合体的声明 1. 像结构体一样&#xff0c;联合体也是由一个或者多个成员构成&#xff0c;这些成员可以不同的类型。 union { 成员1&#xff1b; 成员2&#xff1b; ........ }; //联合体类型 union S {char c;int i; }; 2.联合体的特点和大小计算 像结构体一…