【Spring Cloud】微服务的简单搭建

avatar
作者
猴君
阅读量:2

文章目录

🍃前言

Spring Cloud 提供了⼀些可以让开发⼈员快速构建分布式服务的⼯具,⽐如配置管理, 服务发现,,熔断,智能路由等。他们可以在任何分布式环境中很好的⼯作。

简单来说,Spring Cloud 就是分布式微服务架构的⼀站式解决⽅案,是微服务架构落地的多种技术的集合

既然Spring Cloud是用来解决微服务所带来的问题。那我们接下来就先搭建一个简单的微服务。

本篇内容主要非为以下两点

  1. 开发环境安装
  2. 项⽬搭建

🎄开发环境安装

首先 JDK 版本的选择方面

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

数据库方面,我们选择MySQL

🌳服务拆分的原则

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

微服务的拆分原则主要为以下三个原则:

  1. 单⼀职责原则

  2. 服务⾃治

  3. 单向依赖

🚩单一职责原则

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

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

以电商平台为例,拆分为以下服务
在这里插入图片描述

🚩服务自治

服务⾃治是指每个微服务都应该具备⾼度⾃治的能⼒,即每个服务要能做到独⽴开发,独⽴测试,独⽴构建,独⽴部署,独⽴运⾏.

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

在这里插入图片描述

🚩单向依赖

微服务之间需要做到单向依赖, 严禁循环依赖, 双向依赖
循环依赖:A -> B -> C ->A
双向依赖: A -> B, B -> A

在这里插入图片描述

🍀搭建案例介绍

⼀个完整的电商系统是庞⼤的,搭建微服务困难较大,这里我们实现一的简单的微服务,其中的一部分,以订单列表为例:

大致可拆分为以下两个服务:

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

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

在这里插入图片描述

🌴数据准备

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

🎋工程搭建

🚩构建父子工程

🎈创建父工程

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

在这里插入图片描述

目录结构如下:

在这里插入图片描述

  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</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>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,作用如下

dependencies :将所依赖的jar直接加到项⽬中。⼦项⽬也会继承该依赖. dependencyManagement:只是声明依赖,并不实现Jar包引⼊。如果⼦项⽬需要⽤到相关依赖,需要显式声明。如果⼦项⽬没有指定具体版本,会从⽗项⽬中读取version。如果⼦项⽬中指定了版本号,就会使⽤⼦项⽬中指定的jar版本。此外⽗⼯程的打包⽅式应该是pom,不是jar,这⾥需要⼿动使⽤packaging 来声明

这里需要注意的时,由于后续使用的Spring Cloud 是基于SpringBoot搭建的,所以Spring Cloud 版本与SpringBoot版本有关
在这里插入图片描述

这里使用的咱们项⽬中使⽤的SpringBoot 版本为 3.1.6, 对应的Spring Cloud版本应该为2022.0.x。各位也可以根据自己的版本进行选择。

🎈创建⼦项目-订单服务与商品服务

以下为订单服务的创建过程,商品服务与之一样,名字不同罢了

在这里插入图片描述
在这里插入图片描述

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

<properties>     <maven.compiler.source>17</maven.compiler.source>     <maven.compiler.target>17</maven.compiler.target>     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <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: 9998 spring:  datasource:   url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false   username: 用户   password: 密码   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 #配置驼峰⾃动转换 

实体类:

@Data public class OrderInfo {     private int id;     private int userId;     private int productId;     private int num;     private long price;     private boolean deleteFlag;     private Date createTime;     private Date update_time; } 

mapper:

@Mapper public interface OrderMapper {     @Select("select * from order_detail where id=#{id}")     OrderInfo selectAll(int id); } 

service:

@Service public class OrderService {     @Autowired     private OrderMapper orderMapper;     public OrderInfo selectAll(Integer userid) {         OrderInfo orderInfo = orderMapper.selectAll(userid);         return orderInfo;     } } 

controller:

@RestController @RequestMapping("/order") public class OrderController {     @Autowired     OrderService orderService;     @RequestMapping("/{id}")     public OrderInfo selectAll(@PathVariable("id") int id) {         return orderService.selectAll(id);     } } 

测试访问:http://127.0.0.1:9998/order/1

即可看到以下界面:
在这里插入图片描述

🎈完善商品服务

在这里插入图片描述
启动类:

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

配置文件:

server:  port: 9999 spring:  datasource:   url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false   username: 用户   password: 密码   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 #配置驼峰⾃动转换 

实体类:

@Data public class ProductInfo {     private int id;     private String productName;     private long productPrice;     private boolean state;     private Date createTime;     private Date updateTime; } 

mapper

@Mapper public interface ProductMapper {     @Select("select * from product_detail where id=#{id}")     ProductInfo selectById(int id); } 

service

@Service public class ProductService {     @Autowired     ProductMapper productMapper;     public ProductInfo selectById(int id) {         return productMapper.selectById(id);     } } 

controller

@RestController @RequestMapping("/product") public class ProductController {     @Autowired     ProductService productService;     @RequestMapping("/{id}")     public ProductInfo selectById(@PathVariable("id") int id) {         ProductInfo productInfo = productService.selectById(id);         return productInfo;     } } 

测试:http://127.0.0.1:9999/product/1001
页面返回结果如下:
在这里插入图片描述

🚩远程调用

🎈调用需求

我们一般有如下需求:

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

在这里插入图片描述
意思是,我们希望访问订单页面时,也可以访问到商品服务。

🎈实现

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

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

首先因为我们需要返回商品的信息,所以这里我们需要对我们的实体类进行修改,修改如下:
在这里插入图片描述
在这里插入图片描述

其次,我们创建一个config文件,在其文件下进行以下配置:

@Configuration public class OrderConfig {     @Bean     public RestTemplate restTemplate() {         return new RestTemplate();     } } 

接下来,我们对我们原先的 service 类里面的代码进行修改,修改如下:

@Service public class OrderService {     @Autowired     private OrderMapper orderMapper;     @Autowired     private RestTemplate restTemplate;     public OrderInfo selectAll(Integer userid) {         OrderInfo orderInfo = orderMapper.selectAll(userid);         String url = "http://127.0.0.1:9999/product/" + orderInfo.getProductId();         restTemplate.getForObject(url, ProductInfo.class);         ProductInfo productInfo = restTemplate.getForObject(url,                 ProductInfo.class);         orderInfo.setProductInfo(productInfo);         return orderInfo;     } } 

🎈测试

启动商品服务与订单服务;

访问:http://127.0.0.1:9998/order/1

则会出现以下界面:
在这里插入图片描述

🌲项目存在问题

  • 远程调⽤时,URL的IP和端⼝号是写死的,如果更换IP,需要修改代码 。调⽤⽅如何可以不依赖服务提供⽅的IP?
  • 多机部署,如何分摊压⼒?
  • 远程调⽤时,URL⾮常容易写错,⽽且复⽤性不⾼,如何优雅的实现远程调⽤
  • 所有的服务都可以调⽤该接,是否有⻛险?

  • 除此之外,微服务架构还⾯临很多问题,而Spring Cloud 来就是用来解决这些问题

具体如何解决,后续文章再进行一一介绍

⭕总结

关于《【Spring Cloud】微服务的简单搭建》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!