在初学java时,如果我们写一个订单+商品服务系统,一般会把这两个服务放在同一个项目中,共用一个pom文件和同样的配置信息,两个实例对应的数据表也会放在同一个数据库中。
微服务的由来
随着业务规模增长,单体项目的弊端逐渐暴露:
当流量访问过大时,我们的服务器会因为流量过大而崩溃,造成巨大的损失;
项目功能实现随着生产越来越多,服务器所承载的功能也越来越大,服务和服务之间的耦合性变高,当我们需要改动一处代码时,可能需要改动多处代码,对我们程序员来说其实是很不方便的
如何解决这个问题?
为了解决这个问题,技术大佬们提出了微服务的概念。
微服务的概念
"微"就是把一个庞大的项目拆分成一个个微小的项目 ,一个微服务只负责单一业务模块,实现服务间解耦,提升独立性和可维护性。
很明显,微服务就是把一个大项目拆分成多个微小且独立的项目,部署在不同的服务器上。
服务拆分原则
误区:服务拆分不是越小越好,也不是越细越好,服务越小,微服务架构的优点和缺点就会越来越明显。
服务越小,微服务的独立性越高,同时管理微服务的成本也会增加,所以微服务的拆分要根据场景来判断。
- 单一职责原则
一个服务应该遵循实现一个功能,如果多于一个容易导致类变更问题。 - 服务自洽
一个单独的服务项目应该能够独立进行开发,构建,自主管理,打包,部署······ - 单项依赖
不能存在循环依赖,如:A->B->A 或者 A->B->C->A
以订单系统为例将项目拆分成两个子项目:
商场服务服务可以拆分成两个服务:订单服务+产品服务。
订单服务:通过订单id查询订单信息
产品服务:通过产品id查询产品信息
服务之间通过远程调用通信(后面Nacos+OpenFeign实现):订单服务通过产品id获得产品信息
服务实现流程图:在这里插入图片描述

如何通过代码实现微服务:
准备工作:
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);
2.创建java项目,作为父项目

父项目本身不写业务代码,作为Maven聚合工程,统一管理所有子模块的依赖版本。
3.在父项目中创建子模块
微服务在开发阶段,可以在父项目下创建多个子模块;运行部署阶段,各个子模块打包后就是互相独立的服务,可以部署在不同服务器。


4.在父项目中管理依赖版本号
dependencyMangament 和dependency的区别
- dependencyMangament只是声明依赖,不实现jar包引入,如果子项目需要用到相关依赖,需要显示声明,就会使用子项目中指定的jar版本,同时父工程的打包方式应该是pom,不是jar,这里需要手动使用packaging来声明。
- 使用dependency标签会将该依赖从jar包中引入到项目中,并被子项目继承
删除原来的properties标签,从开始复制,到,声明不复制
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>
springcloud,mybatis,mysql,mybatistest和Springboot 的版本要相对应,详细资料参考官网:https://spring.io/projects/spring-cloud
5.配置子项目依赖
子项目中的依赖互不干扰,独立添加管理
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>
添加lombok依赖
xml
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
配置子项目
- 子项目端口号不能一致,必须单独配置
- 子项目连接的数据库也不一致,需要单独配置
6.创建盒数据表对应的实体类(类中的属性需和表中的字段一一对应)
orderInfo
java
@Data
public class orderInfo {
private Integer id;
private String userId;
private Integer productId;
private Integer num;
private Integer price;
private Integer deleteFlag;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date updateTime;
private productInfo productInfo;
}
productInfo
java
@Data
public class productInfo {
private Integer id;
private String productName;
private Integer productPrice;
private Integer state;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd")
private Date updateTime;
}
业务代码实现:
如何实现远程调用?
使用RestTemplate
Spring3.0开始支持的一个HTTP请求工具,它是一个同步的RESTAPI客户端,提供了常见的REST请求方法的模版。强制使用RSTful风格,处理HTTP连接和关闭,只需要使用者提供资源的地址和参数即可

缺点:
1.接口对外开放,比较危险
2.url固定写死,修改url时较复杂
3.多机部署无法处理
踩坑总结:
1.父项目的pom文件必须设置为pom,不然父项目无法管理依赖。
2.父项目只是作为一个容器统一管理子项目依赖的版本号,没有业务代码实现,也没有启动类
3.远程调用是通过服务端的应用名进行访问,所以在远程调用的代码中,服务端的名称一定要合服务端设置的application.name相匹配
4.在实际,不会把两个项目放在同一个父项目中,因为实际上一个业务是由一个团队负责,这里只是介绍如何创建父子项目