1.了解项目模块的演进
1.1、单体项目
单体项目
单模块:所有的功能模块都写在一个项目中,然后单独部署。
多模块聚合:将一个项目进行拆分为多模块管理
每一层都是一个模块,他们有共同的父工程
1.2、分布式项目
分布式项目
1.2.1 分布式聚合项目(不太推荐)
整个项目根据提供的不同服务来拆分,不同服务可以单独作为一个微服务对象提供访问,服务与服务之间需要相互依赖。
问题:例如下面的 car-service 服务中的 service 来进行远程调用需要引入 rent-service 的 domain 模块依赖,这就会导致若是有大量不同的服务后造成不好梳理的问题。
1.2.2 优化聚合项目的方案推荐
方案一(个人觉得不太行):将原本的多个 mapper、service 以及 web 进行合并为一个 xx-core 模块,xx-domain 单独作为一个模块,其他三层作为一个模块,然后其他服务想要进行调用那么就引入对应的 xx-domain 依赖。
core 和 center 都是核心的意思!
达到的效果:
高内聚:core 和 center 高度集成了本模块的核心内容,别人一般不需要调用
低耦合:其他服务的核心模块一般只需要本服务的 domain 层(实体类),将其单独划分出来即可
一般就是一个微服务里面包含两个模块:xxx-core 模块和 xxx-api 模块
缺点:依赖关系比较多,业务更加复杂
方案二(终极方案):单独构建一个 api 模块服务,然后多个服务去依赖该模块,这种方案当我服务需要创建新的实体时,直接添加到该 api 模块中。
将实体类单独抽离出来作为一个微服务!
问题:可能需要在项目启动过程时多编译几百个类(因为可能有很多实体类我们是不需要的),会让启动速度略慢些。
2.基于 Feign 的工程化项目创建
2.1、案例目标及项目架构图
api 模块依赖 domain 模块,然后其他服务依赖该 api 接口模块。
- domain:实体类。
- api:放置公共接口,例如 feign 接口。
2.2、实战—项目模块搭建
2.2.1、各个模块介绍及父模块 pom.xml
项目模块展示:
注册中心:使用之前 eureka 案例中的即可
接着按照 2.1 中的项目架构图来进行搭建模块,分别是:api 模块、doamin 模块、order 服务模块以及 user 服务模块
暂时不需要 cart 服务模块!
依赖关系如下:
api 模块依赖 domain 模块(在 pom 中进行引入)。
- domain:添加对应的实体类。
- api:主要是编写对应的远程调用 api 接口以及断路器的实现类。
order、user 服务模块依赖 api 模块(在 pom 中进行引入)。
- order 服务主要是对外提供服务。
- user 服务中某个接口会调用 order 服务。
所有的包名统一前缀为 com.changlu。
对于 04-feign-project 父模块的 pom.xml 配置如下:
<?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">
<!-- 父模块依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<!-- 创建好子模块之后,就会自动添加packaging以及modules管理 -->
<groupId>com.changlu</groupId>
<artifactId>04-feign-project</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>domain-project</module>
<module>common-api</module>
<module>user-center</module>
<module>order-center</module>
</modules>
<!--全局版本控制-->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>
<!-- 全局依赖的模块 -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</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>
</dependencies>
</dependencyManagement>
<!-- 打包编译 -->
<!-- <build>-->
<!-- </build>-->
</project>
2.2.2、domain-project 模块
说明:该模块主要放置一些实体类。
pom.xml:
<?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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>com.changlu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>domain-project</artifactId>
</project>
Order.java 实体类:
package com.changlu.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description:
* @Author: changlu
* @Date: 2:30 PM
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {
private Integer orderId;
private String orderName;
private Double price;
}
2.2.3、common-api 模块
说明:这个模块主要放一些公共的 api 接口,例如 feign 接口以及 hystrix 断路器接口。
pom.xml:
<?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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>com.changlu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-api</artifactId>
<dependencies>
<!-- 依赖domain !!! 在这里进行引入 -->
<dependency>
<groupId>com.changlu</groupId>
<artifactId>domain-project</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
com/changlu/feign/UserOrderFeign.java:feign 接口规范
package com.changlu.feign;
import com.changlu.domain.Order;
import com.changlu.feign.hystrix.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @Description:
* @Author: changlu
* @Date: 2:39 PM
*/
@FeignClient(value = "order-service", fallback = UserOrderFeignHystrix.class)
public interface UserOrderFeign {
@GetMapping("/order/{id}")
Order getOrderById(@PathVariable("id")Integer id);
}
com.changlu.feign.hystrix.UserOrderFeignHystrix:对应上面 feign 的断路器
package com.changlu.feign.hystrix;
import com.changlu.domain.Order;
import com.changlu.feign.UserOrderFeign;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Author: changlu
* @Date: 2:43 PM
*/
@Component
public class UserOrderFeignHystrix implements UserOrderFeign {
@Override
public Order getOrderById(Integer id) {
return null;
}
}
2.2.4、order-center 服务模块
说明:该模块主要是对外提供订单服务。
pom.xml:
<?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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>com.changlu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-center</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入api依赖 -->
<dependency>
<groupId>com.changlu</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
配置文件 application.yaml:
server:
port: 8081
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: localhost:8761/eureka
instance:
hostname: localhost
instance-id: ${
eureka.instance.hostname}:${
spring.application.name}:${
server.port}
① 添加启动器注解,开启服务注册
package com.changlu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @Description:
* @Author: changlu
* @Date: 2:36 PM
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
② 创建订单控制器:com.changlu.controller.OrderController
package com.changlu.controller;
import com.changlu.domain.Order;
import com.changlu.feign.UserOrderFeign;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description:
* @Author: changlu
* @Date: 2:38 PM
*/
@RestController
public class OrderController implements UserOrderFeign {
@Override
public Order getOrderById(Integer id) {
Order order = Order.builder()
.orderId(id)
.price(100.0)
.orderName("牛奶布丁").build();
return order;
}
}
2.2.5、user-center 服务模块
说明:该用户服务主要是对外提供用户服务,==在提供用户服务过程中会进行调用订单服务==,其中就涉及到了 openfeign 组件以及 hystrix 断路器组件(需要手动开启)。
实际上这里并没有写 openfeign 和 hystrix 的类,因为在 api 模块中已经写了,这里直接用即可!
pom.xml:
<?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">
<parent>
<artifactId>04-feign-project</artifactId>
<groupId>com.changlu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-center</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入api依赖 -->
<dependency>
<groupId>com.changlu</groupId>
<artifactId>common-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
配置文件 application.yaml:
server:
port: 8082
spring:
application:
name: user-service
eureka:
client:
service-url:
defaultZone: localhost:8761/eureka
instance:
hostname: localhost
instance-id: ${
eureka.instance.hostname}:${
spring.application.name}:${
server.port}
# 开启熔断
feign:
hystrix:
enabled: true
① 创建启动器,添加服务注册以及 feign 包扫描
package com.changlu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Description:
* @Author: changlu
* @Date: 2:52 PM
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.changlu.feign")
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
② 创建用户控制器:com.changlu.controller.UserController
package com.changlu.controller;
import com.changlu.domain.Order;
import com.changlu.feign.UserOrderFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description:
* @Author: changlu
* @Date: 2:53 PM
*/
@RestController
public class UserController {
@Autowired
private UserOrderFeign feign;
@GetMapping("/user/order/{userId}")
public Order getOrderByUserId(@PathVariable("userId")Integer userId) {
//根据用户id获取到订单id
Integer id = 1111;
Order order = feign.getOrderById(id);
return order;
}
}
至此,我们的项目模块搭建完成。
2.3、项目模块测试
我们启动注册中心、订单服务以及用户服务:
接着我们来访问用户服务:localhost:8082/user/order/123
接着我们将订单服务关闭,再此访问查看下:
成功,当订单服务不可用时,即可返回对应断路器的内容!