最近接触了公司内部开发的项目,涉及到了Config配置中心河eureka服务注册中心,以前只用过Nacos服务配置中心,所以边学边试搭建了一个简单的微服务的项目框架,同时记录一下自己踩过的坑。**注:该demo中使用了远程调用服务,所以引入了Feign调用。**
1.从零到一搭建项目
首先创建一个maven项目,在这里我们构建一个父子项目,拆分为四个子项目:order、product、config、eureka,具体的项目结构如下:
父程序pom依赖如下:
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0-RC2</spring-cloud-alibaba.version>
<mybatis-spring-boot-starter.version>3.0.2</mybatis-spring-boot-starter.version>
<spring-boot.version>2.6.4</spring-boot.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<hutool-all.version>5.8.20</hutool-all.version>
<mysql.version>8.0.33</mysql.version>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.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>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool-all.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version> <!-- 最新稳定版本 -->
</dependency>
</dependencies>
</dependencyManagement>
如果要改成自己的版本依赖,需要注意Spring Boot、Spring Cloud和eureka之间的对应关系,建议确定其中两个依赖的版本,然后由Maven自动引入剩余依赖。
2.构建eureka服务
2.1 服务包结构和配置
项目的启动顺序是eureka->config->其他微服务,因为其他服务需要向eureka注册中心注册服务,所以在这里先搭建eureka注册中心。
eureka微服务结构如下:
eureka-server的pom文件如下:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
application.yml:
spring:
application:
name: eureka-server
server:
port: 8761
eureka:
instance:
hostname: 127.0.0.1
client:
register-with-eureka: false
fetch-registry: false
service-url:
default-Zone: http://127.0.0.1:8761/eureka
2.2 eureka配置注意事项
- eureka启动类需要加入@EnableEurekaServer注解。
- eureka服务启动可能出现以下问题:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
该问题出现的原因是引入了spring-boot-starter-jdbc 的依赖,但是并没有正确配置数据库,可以在启动类注解中加入@SpringBootApplication(exclude = DataSourceAutoConfiguration.class),去除掉数据库资源的自动扫描。
- 在启动eureka服务之后,可以通过浏览器访问8761端口的方式来检测服务启动情况,同时在后续其他微服务发起注册之后,可以查看注册情况。
3.构建config配置中心
3.1 服务包结构和配置
config配置中心用来存放其他微服务的配置文件,当其他微服务启动时,从配置中心拉取配置文件,完成服务启动。在该demo中,我将项目配置文件存储到了本地,并没有利用git存储到云端。项目包结构如下:
将其他服务的配置文件放在了config目录下,同时需要在config配置文件中注明路径,xxx-test.yml表示测试环境下的配置,xxx-dev.yml表示开发环境下的配置,xxx-release.yml表示生产环境下的配置。在这里我仅引入了两个服务的dev环境下的配置。
pom文件如下:
<!--eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
applictaion.yml如下,profiles.active=native表示配置文件存储在本地,cloud.config.server.native.search-locations表示配置文件存储的位置,即位于上一级目录的config目录下。
server:
port: 8888
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: file:./config/,classpath:/config/
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
config目录下的配置文件,我仅仅进行了数据库配置, 如下:
spring:
datasource:
username: root
password: ********
url: jdbc:mysql://ip:port/database_name?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-test-query: select 1
connection-timeout: 20000
idle-timeout: 300000
maximum-pool-size: 5
minimum-idle: 5
3.2 config配置注意事项
- 服务端的配置引入的相关依赖为
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
- 注意配置文件的取名方式,通常为{application}-{profile}.yml,application表示服务名,profile表示环境配置,如dev、test等,例如我这里的配置文件取名为order-server-dev.yml。
- 在config服务启动之后,可通过浏览器访问http://ip:port/{application.name}/{profile}查看配置文件,例如:http://localhost:8888/product-server/dev。
4.构建order服务
4.1 order服务包结构和配置
在该项目demo中,order服务远程调用product服务,获取商品名和商品价格。所以应当在order服务启动类上加上@EnableFeignClients。order-server的项目结构如下:
pom文件如下:
<dependencies>
<!--eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 版本可以省略,因为已经在父项目中定义 -->
</dependency>
<!--配置中心客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
配置文件的启动顺序为bootstrap.yml->application.yml->配置中心配置,同时后续的配置会覆盖以前的配置,注意eureka注册服务名均为大写,bootstrap.yml和application.yml文件如下:
bootstrap.yml:
spring:
cloud:
config:
discovery:
enabled: true
service-id: CONFIG-SERVER
name: ${spring.application.name}
profile: ${spring.profiles.active}
fail-fast: true
eureka:
client:
service-url:
default-zone: http://127.0.0.1:8761/eureka
registry-fetch-interval-seconds: 30
application.yml:
server:
port: 8081
spring:
application:
name: order-server
profiles:
active: dev
在bootstrap.yml中的config配置中指定了配置中心的服务id为CONFIG-SERVER,配置中心名称为order-server,配置环境为dev。
4.2 order服务配置注意事项
- 在引入bootstrap.yml后,无法启动项目,并报出如下如下错误,这个错误的原因是,在springcloud2020版本之后,默认禁用了bootstrap配置文件的引入,所以需要我们手动引入相关依赖。
Application failed to start due to an exception
org.springframework.cloud.commons.ConfigDataMissingEnvironmentPostProcessor$ImportException: No spring.config.import set
5. 构建product服务
5.1 product服务包结构和配置
product服务包结构如下:
pom文件如下:
<dependencies>
<!--eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- 版本可以省略,因为已经在父项目中定义 -->
</dependency>
<!--配置中心客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
</dependencies>
bootstrap.yml文件如下:
spring:
cloud:
config:
discovery:
enabled: true
service-id: CONFIG-SERVER
name: ${spring.application.name}
profile: ${spring.profiles.active}
fail-fast: true
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
registry-fetch-interval-seconds: 30
application.yml文件如下:
server:
port: 8080
spring:
application:
name: product-server
profiles:
active: dev
6. 编写product服务代码
6.1 数据库设计
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`price` decimal(10, 2) NOT NULL,
`stock` int(0) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of product
-- ----------------------------
INSERT INTO `product` VALUES (1, 'Laptop', 999.99, 10);
INSERT INTO `product` VALUES (2, 'Smartphone', 499.99, 50);
INSERT INTO `product` VALUES (3, 'Tablet', 299.99, 30);
INSERT INTO `product` VALUES (4, 'Headphones', 79.99, 100);
INSERT INTO `product` VALUES (5, 'Smartwatch', 199.99, 25);
6.2 编写启动类
因为order为服务发起方,product服务为被调用方,所以product服务仅需要添加@SpringBootApplication和@MapperScan两个注解。
6.3 编写商品服务controller层
productController代码如下,编写了一个简单api,通过id获取商品信息
@RestController
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("/api/product/getProduct")
public ProductRespDTO product(@RequestParam("id")Integer id){
return productService.getProductById(id);
}
}
6.4 编写商品服务Service层
ProductService代码如下:
public interface ProductService {
ProductRespDTO getProductById(Integer id);
}
6.5 编写商品服务Service实现层
ProductServiceImpl代码如下,这里没有做复杂的逻辑处理,直接查数据库返回结果。我使用的是mybatis-plus,自带的函数,可以用其他代替。
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO>implements ProductService {
@Override
public ProductRespDTO getProductById(Integer id) {
LambdaQueryWrapper<ProductDO> queryWrapper = Wrappers.lambdaQuery(ProductDO.class)
.eq(ProductDO::getId, id);
ProductDO productDO = baseMapper.selectOne(queryWrapper);
ProductRespDTO result=new ProductRespDTO();
BeanUtil.copyProperties(productDO,result);
return result;
}
}
6.6 编写商品服务Dao层实体和mapper接口
引入lombok,利用在线网站生成实体类,网站地址:凝聚力开发,实体类如下:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName("product")
public class ProductDO {
private Integer id; // 产品 ID
private String name; // 产品名称
private BigDecimal price; // 产品价格
private Integer stock; // 产品库存
}
mapper层接口如下:
@Mapper
public interface ProductMapper extends BaseMapper<ProductDO> {
}
6.7 请求返回实体类
ProductRespDTO实体,仅返回商品的名称和价格。
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProductRespDTO {
private String name; // 产品名称
private BigDecimal price; // 产品价格
}
7. 编写Order服务
7.1 编写订单服务启动类
Order服务需要远程调用Product服务,所以这里涉及到Feign调用。OrderApplication.class如下:
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
7.2 编写订单服务controller层
@RestController
public class OrderController {
@Resource
private ProductFeignClient productFeignClient;
@GetMapping("/api/getOrder")
public ProductRespDTO getProduct(@RequestParam("id")Integer id){
return productFeignClient.getProduct(id);
}
}
7.3 编写订单服务返回实体类
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ProductRespDTO {
private String name; // 产品名称
private BigDecimal price; // 产品价格
}
7.4 编写远程调用接口
注意,因为在eureka服务注册中心默认注册服务名为大写,所以引入也需要用大写,同时注意远程调用的接口路径需要与被调用api的路径一致,具体代码如下:
@FeignClient(value = "PRODUCT-SERVER")
public interface ProductFeignClient {
@GetMapping("/api/product/getProduct")
ProductRespDTO getProduct(@RequestParam("id")Integer id);
}
8. 总结
微服务启动顺序为:eureka->config->order/product。在后续项目启动之后可从eureka注册服务中心查看微服务注册情况,可直接访问对应接口传参进行测试。
例如,可使用apifox或者postman对接口测试,来查看微服务搭建情况:
本文转载自: https://blog.csdn.net/m0_51373428/article/details/141682759
版权归原作者 Passion_yjx 所有, 如有侵权,请联系我们删除。
版权归原作者 Passion_yjx 所有, 如有侵权,请联系我们删除。