一个标准的微服务制作
以一个咖啡小程序项目的订单模块为例,这个模块必将包括:
各种实体类(pojo,dto,vo....)
控制器 controller 服务类service
......
其中控制器中有的接口需要提供给其他微服务,订单模块也需要其他微服务提供接口。
那么在项目开发和维护的过程中,如果一个模块里面的内容发生变动,相关的模块的业务也要修改,有种“牵一发而动全身”的感觉。(如订单模块修改了一个订单pojo里面的一个字段,那么用户模块用到这个订单pojo的所有业务也要同时修改,十分麻烦)
于是乎,我们需要将一个完整的微服务模块拆成三个部分
baoder_orders是项目的订单模块,它被拆成了三个部分
其中:
baoder_orders_model :包含了订单模块要用的所有的实体类
baoder_orders_service :包含了订单模块的所有的业务,它会调用baoder_orders_model
baoder_orders_api :包含了订单模块向其他微服务暴露的接口(基于Feign的声明式的http客户端) 其他微服务可以通过引入 baoder_orders_api 来调用baoder_orders_service的服务
********************************************************************
那么这三个部分是怎么相互协作的呢?且看下文细细道来
*********************************************************
我们先分别分析三个部分的结构,最后再通过最上面的这个图来加深理解
baoder_orders_model
目录结构如下,baoder_orders_model模块用于保存订单模块需要的所有实体类,包括统一封装结果,自定义异常等,异常拦截器仍然放在baoder_orders_service里面。
打开它的pom文件,可以看到它确确实实只是作为存储实体类的模块,甚至连bulid标签都没有
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_model</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>baoder_orders_model</name>
<description>baoder_orders_model</description>
<!-- baoder_parent用于管理项目需要的所有的依赖的版本-->
<parent>
<groupId>com.gmgx</groupId>
<artifactId>baoder_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!--模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
</project>
我们需要做的事情就是让他被service和api引用,那么就需要把它安装到我们本地的maven仓库里面,需要的时候作为依赖在pom文件中引用即可。
执行完这三步后,去我们本地的maven仓库所在的文件夹里面,可以看到打包好的baoder_orders_model依赖
baoder_orders_service
目录结构如下,baoder_orders_service包含了订单模块的所有业务,这个模块是要被打包发布到docker上的,所以会有有关docker的两个文件
打开application.properties文件,可以看到关于数据库,nacos,redis的相关配置。
其中数据库和redis是业务需要的
同时利用nacos将这个微服务注册到nacos注册中心上,服务名为baoder-orders-service
# 应用服务 WEB 访问端口
server.port=8090
# 数据库配置
spring.datasource.url=jdbc:mysql://192.168.2.222:3306/baoder-orders?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&connectTimeout=10000&socketTimeout=30000&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=baoder-orders-service
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=192.168.2.222:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
# redis配置
spring.data.redis.host=192.168.2.222
spring.data.redis.database=0
(重头戏)打开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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>baoder_orders_service</name>
<description>baoder_orders_service</description>
<parent>
<groupId>com.gmgx</groupId>
<artifactId>baoder_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.gmgx.BaoderOrdersServiceApplication</mainClass>
<skip>false</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- 跳过测试-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
可以看到我们刚才install到本地仓库的baoder_orders_model
我们随便打开一个服务,能看到baoder_orders_model里面的实体类已经被引进来了
这样一来,负责其他模块的同事们只需要拿到最新的baoder_orders_model,就可以愉快地做自己的模块了,只需要我把最新的 baoder_orders_model给他们,让他们在他们的本地仓库install就可以了。
baoder_orders_api(被调用方)
目录结构如下。baoder_orders_api 负责向其他调用者(其他微服务)提供baoder_service_api暴露出来的接口,我们需要详细说明这个模块是怎么做的,这就是SpringCloud中Feign组件的应用。
1.引入依赖
我们需要引入如下依赖(这两个依赖是feign要用的)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
最终的pom文件是这样的,声明http请求需要实体类,因此引入了baoder_orders_model
其实只需要三个依赖(2+1)就行了,boot的依赖调用者肯定会有
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>baoder_orders_api</name>
<description>baoder_orders_api</description>
<parent>
<groupId>com.gmgx</groupId>
<artifactId>baoder_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</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.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.gmgx.BaoderOrdersApiApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
2.写客户端接口
package com.gmgx.api;
import com.gmgx.common.Result;
import com.gmgx.dto.OrderSearchDto;
import com.gmgx.dto.SubmitDto;
import com.gmgx.entity.Orders;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
@FeignClient(
name = "baoder-orders-service",
contextId = "baoder-orders-service"
)
@Component
public interface OrdersClient {
static final String prefix = "order";
@PutMapping(prefix + "/cancel")
public Result<Object> cancel(@RequestBody Orders order);
@PutMapping(prefix + "rejection")
public Result<Object> rejection(@RequestBody Orders order);
@PutMapping(prefix + "confirm")
public Result<Object> confirm(@RequestBody Orders order);
@PutMapping(prefix + "delivery/{id}")
public Result<String> delivery(@PathVariable(value = "id") String id);
@PutMapping(prefix + "complete/{id}")
public Result<Object> complete(@PathVariable(value = "id") String id);
@PostMapping(prefix + "conditionSearch")
public Result<Object> conditionSearch(@RequestBody OrderSearchDto dto);
@GetMapping(prefix + "details/{oId}")
public Result<Object> getDetails(@PathVariable(value = "id") String oId);
@PutMapping(prefix + "cancel/{id}/{cancelReason}")
public Result<Object> cancelByMember(@PathVariable("id") String id, @PathVariable(value = "cancelReason") String cancelReason);
@GetMapping(prefix + "historyOrders/{memberId}")
public Result<Object> getHistoryOrders(@PathVariable(value = "memberId") String memberId);
@PostMapping(prefix + "submit")
public Result<String> submit(@RequestBody SubmitDto submitDto);
@PutMapping(prefix + "payment/{oId}/{type}")
public Result<Object> payment(@PathVariable(value = "oId") String oId, @PathVariable(value = "type") String type);
@PostMapping(prefix + "repetition/{oId}")
public Result<Object> repetition(@PathVariable(value = "oid") String oId);
}
*************************************************************************************************************
@FeignClient用于声明要调用哪个service,这个从service的配置文件里面找,即nacos注册的服务名(所有的微服务都要注册到nacos)
要把这个接口注册成一个组件,这样调用方就可以通过@Resource 自动装配使用Feign客户端了
feign客户端的接口路径,访问方法,请求参数,返回值等要和service那边的控制器一样
最后,baoder_orders_api也要被install到本地仓库,我们只需要把这个模块给需要调用我们接口的同事就可以了
调用api
现在用一个demo-useOrders项目来模拟baoder_orders_service的调用方
创建好项目后,打开它的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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gmgx</groupId>
<artifactId>demo-useOrders</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-useOrders</name>
<description>demo-useOrders</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.0.2</spring-boot.version>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
<spring-cloud.version>2022.0.0-RC2</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gmgx</groupId>
<artifactId>baoder_orders_model</artifactId>
<version>0.0.1-SNAPSHOT</version>
</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.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.gmgx.DemoUseOrdersApplication</mainClass>
<skip>false</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
导入的依赖如下(注意导入了api和model)
控制器中直接注入就可以使用feign客户端了
applicaton.properties如下
# 应用服务 WEB 访问端口
server.port=8088
# Nacos帮助文档: https://nacos.io/zh-cn/docs/concepts.html
spring.application.name=useOrders-service
# Nacos认证信息
spring.cloud.nacos.discovery.username=nacos
spring.cloud.nacos.discovery.password=nacos
# Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
spring.cloud.nacos.discovery.server-addr=192.168.2.222:8848
# 注册到 nacos 的指定 namespace,默认为 public
spring.cloud.nacos.discovery.namespace=public
# 数据库配置
spring.datasource.url=jdbc:mysql://192.168.2.222:3306/baoder-orders?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&connectTimeout=10000&socketTimeout=30000&allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
现在把service和demo-useOrders都跑起来(我服务器已经部署了baoder_orders_service容器和demo-useOrders,但是本地连服务器访问会超时,干脆直接启动本地的service模块,效果是一样的)
***********************************************************************************************************
最后再回到这张图,思路是否清晰了点,这就是一个标准的微服务的制作流程。(其中baoder_orders_api和demo-useOrders那里用到了SpringCloud的feign组件)