如前所述,服务治理主要包含注册中心,前面所有的微服务子工程均已注册到nacos,然后由相应的应用到nacos进行拉取。
接下来,我们将使用nacos的配置中心,用于保存和分发应用运行所需的动态配置。比如数据库的各类信息等。
一、nacos中新建配置
nacos新建配置
选择"配置列表菜单",点击"+"号。

如图所示,新建一个配置,点击"发布"。

点击"确定"之后,可以在配置列表看到配置结果。

二、server-goods服务中发现配置
1.修改pom,添加config依赖和bootstrap依赖
xml
<!--config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 开启bootstrap上下文 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
2. 新建bootstrap.yml文件,添加nacos配置

yaml
spring:
cloud:
nacos:
config:
server-addr: 10.50.41.17:8848
file-extension: yaml
application:
name: goods
特别说明:因为目前我使用的是SpringBoot 2.6.3 + SCA 2021.0.1.0(老版本),所以使用的是,原生支持 bootstrap.yml 方式。使用原生方式是因为,其实大家会发现最近这两年,随着AI的兴起,真正古法编程的原来越少,相关的资料也还是停留在以前的版本,所以,为了大家看资料的时候不至于搞不清卡,这里还是使用了bootstrap的方式。 *
也可以
3. 到controller测试是否能够找到配置内容

添加如下代码:
java
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
说明:经过前面的配置后,项目启动后先到bootstrap中找到nacos配置,然后到 Nacos 远程拉配置,获取到config.info,最后放到 Spring 环境里。
4.启动项目运行
地址栏输入http://localhost:8100/config/info,显示配置中心config.info信息,说明整个配置中心能够正常运行。

** 特别注意:之所以使用配置中心就是为了将比如数据库等这些设置统一管理,需要修改的时候,只要在配置中心统一修改即可,并且能即时生效,而不需要所有服务重新启动。所以,需要在controller添加一个自动更新配置的注解@RefreshScope**

三、设计数据库
如前所述,所有微服务的配置(比如数据库 url、redis 密码、功能开关)都集中存在配置中心;
- 微服务启动时,从配置中心拉取自己需要的配置; 配置需要修改时,直接在配置中心改,所有相关服务可动态刷新配置(不用重启服务)。
- 接下来,我们实战一个数据库的配置。
1.新建数据库db2025
2.新建数据表
(1)tb_stock
sql
CREATE TABLE `tb_stock` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
`goods_id` BIGINT NOT NULL COMMENT '商品id,关联商品表',
`stock_num` INT NOT NULL DEFAULT 0 COMMENT '可用库存数量',
`locked_num` INT NOT NULL DEFAULT 0 COMMENT '锁定库存(下单未支付,防超卖)',
`total_num` INT NOT NULL DEFAULT 0 COMMENT '总库存 = 可用+锁定',
`warehouse_id` BIGINT DEFAULT 1 COMMENT '仓库id,简单版可以先固定1(默认仓库)',
`version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号(扣库存防并发超卖,重点!)',
`create_time` DATETIME NULL COMMENT '创建时间',
`update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
UNIQUE KEY `uk_goods_warehouse` (`goods_id`,`warehouse_id`) -- 一个商品在同一个仓库只能一条库存记录
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品库存表';
(2)tb_goods
sql
CREATE TABLE `tb_goods` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '商品id',
`goods_name` VARCHAR(128) NOT NULL COMMENT '商品名称',
`price` DECIMAL(10,2) NOT NULL COMMENT '售价',
`pic` VARCHAR(255) DEFAULT '' COMMENT '商品图片',
`status` TINYINT DEFAULT 1 COMMENT '状态:0下架 1上架',
`create_time` DATETIME NULL COMMENT '创建时间',
`update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
3.添加数据
sql
INSERT INTO tb_goods(id,goods_name,price,status,create_time) VALUES
(1,'小米手机14',3999.00,1,now()),
(2,'华为平板',2599.00,1,now()),
(3,'机械键盘',129.00,1,now());
INSERT INTO tb_stock(goods_id,stock_num,locked_num,total_num,version,create_time) VALUES
(1,100,0,100,0,now()),
(2,50,0,50,0,now()),
(3,200,0,200,0,now());
四、到server-store子工程中进行nacos中的数据库配置拉取
1.修改server-store子工程中的pom文件
添加数据库、config、openfeign 、bootstrap、lombok等的依赖
xml
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- MySQL驱动,适配 MySQL5.7 用 8.0.28 没问题 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- MySQL驱动,适配 MySQL5.7 用 8.0.28 没问题 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 开启bootstrap上下文 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2. 添加bootstrap.yml文件

yaml
spring:
application:
name: store # ⭐服务名,Feign 里写的就是这个
cloud:
nacos:
config:
server-addr: 10.50.41.17:8848 # 换成自己的 Nacos 地址
file-extension: yaml
prefix: server-store # 覆盖默认name,去拉 server-store.yml
group: DEFAULT_GROUP
3. nacos中进行数据库配置
- DataId:
server-store.yml - Group:
DEFAULT_GROUP - 配置格式:YAML

yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2025?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
username: root
password: root
hikari:
maximum-pool-size: 10
minimum-idle: 2
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
4. 启动类添加 @MapperScan("com.example.mapper")
5.添加实体类

java
package com.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("tb_stock")
public class Stock {
@TableId(type = IdType.AUTO)
private Long id;
private Long goodsId;
private Integer stockNum;
private Integer lockedNum;
private Integer totalNum;
private Long warehouseId;
private Integer version;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
6.添加Mapper接口
java
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Stock;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface StockMapper extends BaseMapper<Stock> {
}

7.添加service
(1)添加接口

java
package com.example.service;
import com.example.entity.Stock;
public interface StockService {
/** 按商品id查询库存 */
Stock getByGoodsId(Long goodsId);
/** 按库存主键查询 */
Stock getById(Long id);
}
(2)添加实现类

java
package com.example.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.entity.Stock;
import com.example.mapper.StockMapper;
import com.example.service.StockService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class StockServiceImpl implements StockService {
@Resource
private StockMapper stockMapper;
@Override
public Stock getByGoodsId(Long goodsId) {
// 一个商品在一个仓库一条记录,用 selectOne
return stockMapper.selectOne(
Wrappers.<Stock>lambdaQuery()
.eq(Stock::getGoodsId, goodsId)
.eq(Stock::getWarehouseId, 1L)
);
}
@Override
public Stock getById(Long id) {
return stockMapper.selectById(id);
}
}
8.修改StoreController
原来这个类中只是进行了nacos服务接口测试。
现在添加两个业务逻辑,一个是按商品id查询库存,这个对应的是goods这个服务中的需求。另一个是自身查找商品。
具体代码如下:
java
package com.example.controller;
import com.example.entity.Stock;
import lombok.extern.slf4j.Slf4j;
import com.example.service.StockService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@Slf4j
@RestController
@RequestMapping("/store")
public class StoreController {
@Value("${server.port}")
private String port;
@Resource
private StockService stockService;
/**
* 按商品id查询库存
* 对应 goods 的 Feign:@GetMapping("/store/{id}")
*/
@GetMapping("/{id}")
public Stock getStock(@PathVariable("id") Long goodsId) {
log.info("库存服务端口:{},收到查询商品id:{}", port, goodsId);
Stock stock = stockService.getByGoodsId(goodsId);
if (stock == null) {
// 查不到返回空壳,避免 consumer 端 null 反序列化异常
Stock empty = new Stock();
empty.setGoodsId(goodsId);
empty.setStockNum(0);
return empty;
}
return stock;
}
/** 按库存记录主键查询 */
@GetMapping("/id/{id}")
public Stock getByStockId(@PathVariable("id") Long id) {
return stockService.getById(id);
}
/** 返回端口,单纯用来验证负载均衡 */
@GetMapping("/port")
public String port() {
return "库存服务端口:" + port;
}
}
五、修改server-goods调用server-store中的接口
1.修改server-goods中的接口StoreFeignClient

2.修改server-goods中的controller业务逻辑index

六、重新启动运行
(1)首先运行server-store的两个示例(8084、8085)
(2)再运行server-goods(8100)
nacos中服务列表有两个,如图所示:


(3)地址栏输入:http://localhost:8100/config/info,运行效果如下:

(4)地址栏输入:http://localhost:8085/store/1,按商品id搜索,测试server-store运行效果:

(5)地址栏输入:http://localhost:8085/store/id/2,按库存记录主键搜索

(6)地址栏输入:http://localhost:8100/index,按商品id(传递值目前是一个固定值,正常项目由前端传递:1)利用feign实现服务消费调用。

七、在server-goods中再次查看openfeign是否实现了负载均衡
为了清楚的看到负载均衡的效果,我们添加一点内容输出调用port。
1.到serv-store中修改stock实体类
添加一个临时字段port,注意:这个字段非数据表中字段,所以应该添加注解:@TableField(exist = false)

2.修改StoreController
修改StoreController,将获取的端口号写入到stock对象中。

3.重新运行server-store和server-goods
重新运行上述三个服务。

刷新地址栏:http://localhost:8100/index,可以看到port:8085和port:8084实现轮询。
