前面两篇讲了不用springcloud做粘合,直接springboot+dubbo+nacos+sentinel的搭建
这篇讲用springcloud做粘合是怎么搞的
一、nacos作为注册中心
1.原理
2.调用方business服务代码
场景是business项目调用storage项目
目录
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>test-spring-cloud-alibaba</artifactId>
<groupId>com.sid</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>business</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
<!-- 这个是接口包 -->
<dependency>
<groupId>com.sid</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 不加这个不行,好像是给nacos上报用的http协议 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.1.RELEASE</version>
</plugin>
<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<!-- 这样就不用再generatorConfig.xml里面配置本地的mysql-connector-java位置了-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
bootstrap.yml
spring:
application:
name: app-business
cloud:
nacos:
# Nacos 服务发现与注册配置
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
application.yml
server:
port: 8085
###起个名字作为服务名称(该服务注册到nacos注册中心的名称,比如订单服务)
spring:
application:
name: app-business
datasource:
url: jdbc:mysql://localhost:3306/test-business
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
# Dubbo Application
dubbo:
# Dubbo Protocol
protocol:
name: dubbo
## Random port
port: -1
## Dubbo Registry
registry:
address: spring-cloud://localhost
application:
name: app-business
scan:
#这里是表明RPC接口包的package
base-packages: com.sid.rpc.service
cloud:
#business这个服务需要调用order和storage服务,这个配置表示去订阅他们
subscribed-services: app-order,app-storage
## 该配置节点为独立的节点,不是在在spring的节点下
mybatis:
mapper-locations: classpath:mapping/*.xml #注意:一定要对应mapper映射xml文件的所在路径
type-aliases-package: com.sid.model # 注意:对应实体类的路径
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制台打印sql
# log config
logging:
config: classpath:logback-spring.xml
启动类
package com.sid;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
@MapperScan("com.sid.mapper")
public class BusinessApp {
public static void main(String[] args) {
SpringApplication.run(BusinessApp.class, args);
}
}
BusinessServiceImpl.java
该类里面就是用dubbo去RPC调用storage服务和order服务
package com.sid.service;
import com.sid.mapper.BusinessMapper;
import com.sid.model.Business;
import com.sid.rpc.service.model.Order;
import com.sid.rpc.service.service.OrderServiceApi;
import com.sid.rpc.service.service.StorageServiceApi;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;
@Service
public class BusinessServiceImpl implements BusinessService{
@Reference(check = false,group = "order-provider", version = "1.0.0")
OrderServiceApi orderServiceApi;
@Reference(check = false,group = "storage-provider", version = "1.0.0")
StorageServiceApi storageServiceApi;
@Resource
BusinessMapper businessMapper;
/**
* 采购
*/
@Transactional
@Override
public void purchase(String userId, String commodityCode, int orderCount) {
Business b = new Business();
b.setUserId(userId);
b.setCommodityCode(commodityCode);
b.setOrderCount(orderCount);
int i = businessMapper.insertSelective(b);
String storageDeductResult = storageServiceApi.deduct(commodityCode, orderCount);
System.out.println(storageDeductResult);
Order order = orderServiceApi.create(userId, commodityCode, orderCount);
String orderCreateResult = order.getMsg();
if(!storageDeductResult.equals("success") || !orderCreateResult.equals("success")){
System.out.println("resutl storage service :"+storageDeductResult);
System.out.println("resutl order service :"+orderCreateResult);
throw new RuntimeException("BusinessServiceImpl purchase fail");
}
}
}
2.被调用方storage服务代码
目录
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>test-spring-cloud-alibaba</artifactId>
<groupId>com.sid</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>storage</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.sid</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.1.RELEASE</version>
</plugin>
<!-- mybatis generator 自动生成代码插件 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
<verbose>true</verbose>
</configuration>
<dependencies>
<!-- 这样就不用再generatorConfig.xml里面配置本地的mysql-connector-java位置了-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
bootstrap.yml
spring:
application:
name: app-storage
cloud:
nacos:
# Nacos 服务发现与注册配置
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
application.yml
server:
port: 8083
###起个名字作为服务名称(该服务注册到nacos注册中心的名称,比如订单服务)
spring:
application:
name: app-storage
datasource:
url: jdbc:mysql://localhost:3306/test-storage
username: root
password: root
# 使用druid数据源
driver-class-name: com.mysql.jdbc.Driver
cloud:
nacos:
# Nacos 服务发现与注册配置
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
# Dubbo Application
dubbo:
# Dubbo Protocol
protocol:
name: dubbo
## Random port
port: -1
## Dubbo Registry
registry:
# 挂载到 Spring Cloud 注册中心
address: spring-cloud://localhost
application:
name: app-storage
scan:
base-packages: com.sid.rpc.service
## 该配置节点为独立的节点,不是在在spring的节点下
mybatis:
mapper-locations: classpath:mapping/*.xml #注意:一定要对应mapper映射xml文件的所在路径
type-aliases-package: com.sid.model # 注意:对应实体类的路径
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制台打印sql
启动类
package com.sid;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.sid.mapper")
@EnableDubbo
public class StorageApp {
public static void main(String[] args) {
SpringApplication.run(StorageApp.class, args);
}
}
rpc实现类
package com.sid.rpc.service;
import com.sid.rpc.service.service.StorageServiceApi;
import com.sid.service.StorageService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
@Service(group = "storage-provider", version = "1.0.0")
public class StorageServiceRpcImpl implements StorageServiceApi {
@Autowired
private com.sid.service.StorageService StorageService;
/**
* 商品扣库存
* */
@Override
public String deduct(String commodityCode, int count) {
return StorageService.deduct(commodityCode,count);
}
}
业务service实现类
package com.sid.service;
import com.sid.mapper.StorageMapper;
import com.sid.model.Storage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class StorageServiceImpl implements StorageService {
@Resource
private StorageMapper storageMapper;
/**
* 商品扣库存
* */
@Override
@Transactional
public String deduct(String commodityCode, int count) {
Storage storage = storageMapper.selectByPrimaryKey(commodityCode);
Integer storageCount = storage.getStorageCount();
storage.setStorageCount(storageCount - count);
int i = storageMapper.updateByPrimaryKey(storage);
//int ii = 1/0;
if(i == 1){
return "success";
}
return "fail";
}
}
3 RPC接口包服务代码
StorageServiceApi.java
package com.sid.rpc.service.service;
public interface StorageServiceApi {
String deduct(String commodityCode, int count);
}
4 启动nacos,启动后这两个服务
二、nacos作为配置中心
1.原理
1.Nacos Config 数据结构
Nacos Config 主要通过 dataId 和 group 来唯一确定一条配置.
Nacos Client 从 Nacos Server 端获取数据时,调用的是此接口 ConfigService.getConfig(String dataId, String group, long timeoutMs)。
2.Spring Cloud 应用获取数据
dataId
在 Nacos Config Starter 中,dataId 的拼接格式如下
${prefix} - ${spring.profiles.active} . ${file-extension}
prefix 默认为 spring.application.name 的值,也可以通过springboot的application.yml中的配置项 spring.cloud.nacos.config.prefix来配置。
spring.profiles.active 即为当前环境对应的 profile
注意,当 spring.profiles.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成
${prefix}.{file-extension}
file-extension 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension来配置。 默认是 properties 类型。
group
group 默认为 DEFAULT_GROUP,可以通过 spring.cloud.nacos.config.group 配置。
默认情况下,会加载命名空间=public,DataID=${spring.application.name}.properties,Group=DEFAULT_GROUP的配置。
3.自动注入
Nacos Config Starter 实现了 org.springframework.cloud.bootstrap.config.PropertySourceLocator接口,并将优先级设置成了最高。
在 Spring Cloud 应用启动阶段,会主动从 Nacos Server 端获取对应的数据,并将获取到的数据转换成 PropertySource 且注入到 Environment 的 PropertySources 属性中,
所以使用 @Value 注解也能直接获取 Nacos Server 端配置的内容。
2.读nacos命名空间public里面的配置
默认情况下,会加载命名空间=public,DataID=${spring.application.name}.properties,Group=DEFAULT_GROUP的配置。
作为配置中心举例就只以business项目举例了
business项目在前面的代码基础上,加入下面的改动
pom.xml加入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
bootstrap.yml加入
获取配置中心的配置
这里可以看到,直接用spring的@Value就能读到nacos配置中心的配置了(前提是用springcloud来粘合的)
Nacos Config Starter 实现了 org.springframework.cloud.bootstrap.config.PropertySourceLocator接口,并将优先级设置成了最高。
在Spring Cloud应用启动阶段,会主动从Nacos Server端获取对应的数据,并将获取到的数据转换成PropertySource且注入到Environment的PropertySources属性中,所以使用@Value注解也能直接获取Nacos Server端配置的内容。
package com.sid.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class NacosConfig {
@Value("${business.testConfig}")
private String businessTestConfig;
@Value("${ip}")
private String ip;
public String getBusinessTestConfig() {
return businessTestConfig;
}
public void setBusinessTestConfig(String businessTestConfig) {
this.businessTestConfig = businessTestConfig;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
这样子搞,是读的nacos的命名空间public里面的东西
默认情况下,会加载命名空间=public,DataID=${spring.application.name}.properties,Group=DEFAULT_GROUP的配置。
在配置列表public命名空间里面创建1个文件,文件的名字是app-business,这个文件的名字要跟该应用的名字一样,文件格式是properties
app-business内容是
启动的时候从日志就能看到本应用读的是nacos配置中的哪个文件,文件里面有些什么内容
写个controller来请求验证一下读到的配置文件的内容
package com.sid.controller;
//import com.sid.config.NacosConfig;
import com.sid.config.NacosConfig;
import com.sid.model.Business;
import com.sid.service.BusinessService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class BusinessController {
@Autowired
BusinessService businessService;
@Autowired
NacosConfig nacosConfig;
/**
* 采购
*/
@RequestMapping(value = "business/purchase",method = RequestMethod.POST)
public void purchase(String userId, String commodityCode, int orderCount) {
businessService.purchase(userId,commodityCode,orderCount);
}
@RequestMapping(value = "business/config",method = RequestMethod.GET)
public NacosConfig config() {
return nacosConfig;
}
}
3.nacos配置中心以命名空间区分开发、测试环境不同的配置
在nacos的界面创建2个命名空间,一个是dev,
在配置列表中,dev命名空间下,创建配置文件app-business
内容如下
代码
主要是修改bootstrap.yml的配置
加上namespace,注意这里的命名空间不是用名字,不是用dev,而是用它的ID,命名空间的ID
启动日志其实就能看到读到了dev命名空间里面的app-business配置文件中的东西了
4.多个服务共用一个公共的配置文件
在nacos的管理界面加入配置文件application.properties,注意要把后缀.properties写全
内容
主要是修改bootstrap.yml的配置,加一个配置,注意要把后缀.properties写全
启动日志
可以看到去加载了文件application.properties
2021-05-27 16:55:40.361 WARN --- [main] c.a.c.n.c.NacosPropertySourceBuilder Ignore the empty nacos configuration and get it based on dataId[application.properties] & group[DEFAULT_GROUP]
2021-05-27 16:55:40.369 INFO --- [main] c.a.c.n.c.NacosPropertySourceBuilder Loading nacos data, dataId: 'app-business', group: 'DEFAULT_GROUP', data: business.testConfig=dev
ip=12345678
2021-05-27 16:55:40.373 WARN --- [main] c.a.c.n.c.NacosPropertySourceBuilder Ignore the empty nacos configuration and get it based on dataId[app-business.properties] & group[DEFAULT_GROUP]
2021-05-27 16:55:40.373 INFO --- [main] b.c.PropertySourceBootstrapConfiguration Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='app-business.properties'}, NacosPropertySource {name='app-business'}, NacosPropertySource {name='application.properties'}]}
5.动态刷新配置
Nacos Config Starter 默认为所有获取数据成功的 Nacos 的配置项添加了监听功能,在监听到服务端配置发生变化时会实时触发 org.springframework.cloud.context.refresh.ContextRefresher 的 refresh 方法 。
如果需要对 Bean 进行动态刷新,给类添加 @RefreshScope 或 @ConfigurationProperties注解。
6.Endpoint 信息查看(结合spring-boot-starter-actuator)
Springboot支持这一点,Nacos Config也同时可以使用Endpoint来暴露信息。
在maven 中添加 spring-boot-starter-actuator依赖,并在配置中允许 Endpoints 的访问。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
Spring Boot 1.x 中添加配置 management.security.enabled=false
Spring Boot 2.x 中添加配置 management.endpoints.web.exposure.include=*
management:
endpoints:
web:
exposure:
include: '*'
Spring Boot 1.x 可以通过访问 http://127.0.0.1:8085/nacos_config 来查看 Nacos Endpoint 的信息。
Spring Boot 2.x 可以通过访问 http://localhost:8085/actuator/nacos-config 来访问。
在idea中能看到一共有哪些mapping
服务发现的
http://127.0.0.1:8085/actuator/nacos-discovery
三、nacosAPI及客户端的配置相关配置
配置
nacos作为配置中心,客户端的配置见源码中的NacosConfigProperties类
nacos作为注册中心,客户端的配置见源码中的NacosDiscoveryProperties类
openApi
https://nacos.io/zh-cn/docs/open-api.html
比如查询服务列表
http://localhost:8848/nacos/v1/ns/service/list?pageNo=1&pageSize=100
获取配置
http://localhost:8848/nacos/v1/cs/configs?dataId=app-business&group=DEFAULT_GROUP
发布配置
curl -X POST 'http://127.0.0.1:8848/nacos/v1/cs/configs' -d 'dataId=nacos.example&group=com.alibaba.nacos&content=contentTest'
四、nacos用mysql作为存储
1.初始化数据库
Nacos的数据库脚本文件在我们下载Nacos-server时的压缩包中就有。
进入nacos-server-1.4.1\nacos\conf目录,里面有个初始化文件:nacos-mysql.sql。
自己在mysql中创建一个nacos 的数据库,然后执行nacos-mysql.sql初始化脚本,成功后会生成一些表。
2.修改配置文件
这里是需要修改Nacos-server的配置文件
Nacos-server其实就是一个Java工程或者说是一个Springboot项目,他的配置文件在nacos-server-1.4.1\nacos\conf目录下,名为 application.properties
把配置文件中关于mysql配置的部分打开,本来是注释了的,填入自己的mysql的IP账号密码:
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql### Count of DB:
db.num=1### Connect URL of DB:
db.url.0=jdbc:mysql://localhost:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root### Connection pool configuration: hikariCP
db.pool.config.connectionTimeout=30000
db.pool.config.validationTimeout=10000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=2
3.启动Nacos
先启动Nacos-server,在目录nacos-server-1.4.1\nacos\bin
cmd startup.cmd -m standalone
启动成功后进入Nacos控制台,此时的Nacos控制台中焕然一新,之前的数据都不见了
因为加入了新的数据源,Nacos从mysql中读取所有的配置文件,而我们刚刚初始化的数据库是干干净净的,自然不会有什么数据和信息显示。
4.验证是否持久化到数据库中
添加一些配置
观察数据库nacos中的数据库表 config_info , 如下