vue-java-tutorials/cloud-demo/ReadMe.md

338 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Nacos注册中心
## 注册中心
### 服务发现
发现服务信息。
```java
@SpringBootTest()
public class DiscoveryTest {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private NacosDiscoveryClient nacosDiscoveryClient;
@Test
void discoveryClientTest() {
for (String service : discoveryClient.getServices()) {
System.out.println(service);
for (ServiceInstance instance : discoveryClient.getInstances(service)) {
System.out.println("IP地址" + instance.getHost());
System.out.println("端口号" + instance.getPort());
}
}
System.out.println("----------------------------------------------");
// 两个方式一样DiscoveryClient 是 Spring自带的 NacosDiscoveryClient是 Nacos
for (String service : nacosDiscoveryClient.getServices()) {
System.out.println(service);
for (ServiceInstance instance : nacosDiscoveryClient.getInstances(service)) {
System.out.println("IP地址" + instance.getHost());
System.out.println("端口号" + instance.getPort());
}
}
}
}
```
### 远程调用
订单模块调用远程商品模块使用了nacos可以使用`RestTemplate`,其中`RestTemplate`是线程安全的,只要注册一次全局都是可以使用。
**RestTemplate源码**
继承了`InterceptingHttpAccessor`,在`InterceptingHttpAccessor`中,使用了单例模式。
```java
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = this.getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
} else {
return super.getRequestFactory();
}
}
```
#### 实现远程调用
##### 普通方式调用
注册`RestTemplate`
```java
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
```
如果我们的服务启动了多个,在下面代码中即使一个服务宕机也可以做到远程调用。
```java
private Product getProductFromRemote(Long productId) {
// 获取商品服务所有及其的 IP+port
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance instance = instances.get(0);
// 远程URL
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/product/" + productId;
// 2. 远程发送请求
log.info("远程调用:{}", url);
return restTemplate.getForObject(url, Product.class);
}
```
##### 负载均衡调用
注册`RestTemplate`
```java
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
```
使用负载均衡`LoadBalancerClient`,通过负载均衡算法动态调用远程服务。
```java
/**
* 远程调用商品模块 --- 负载均衡
*
* @param productId 商品id
* @return 商品对象
*/
private Product getProductFromRemoteWithLoadBalancer(Long productId) {
// 1. 获取商品服务所有及其的 IP+port
ServiceInstance instance = loadBalancerClient.choose("service-product");
// 远程URL
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/product/" + productId;
// 2. 远程发送请求
log.info("负载均衡远程调用:{}", url);
return restTemplate.getForObject(url, Product.class);
}
```
##### 负载均衡注解调用
> [!TIP]
>
> 如果远程注册中心宕机是否可以调用?
>
> 调用过:远程调用不在依赖注册中心,可以通过。
>
> 没调用过:第一次发起远程调用;不能通过。
在`RestTemplate`上加上`@LoadBalanced`注解使用负载均衡。
```java
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
```
在实际的调用中并不需要再显式调用将URL替换成服务名称即可。
```java
/**
* 远程调用商品模块 --- 负载均衡注解调用
*
* @param productId 商品id
* @return 商品对象
*/
private Product getProductFromRemoteWithLoadBalancerAnnotation(Long productId) {
// 远程URL实现动态替换
String url = "http://service-product/api/product/" + productId;
// 远程发送请求
log.info("负载均衡注解调用:{}", url);
return restTemplate.getForObject(url, Product.class);
}
```
## 按需加载
### 数据隔离架构
![数据隔离架构图](./images/image-20250526160251551.png)
### 命名空间管理
#### 创建命名空间
> [!TIP]
> 预配置的命名空间示例可在项目 `samples/namespace-config` 目录下找到。
建议创建以下标准命名空间:
- `dev` - 开发环境
- `test` - 测试环境
- `prod` - 生产环境
**操作步骤**
1. 进入Nacos控制台命名空间管理
2. 点击"新建命名空间"
3. 填写命名空间信息ID和名称
![创建命名空间界面](./images/image-20250526160457765.png)
![命名空间列表](./images/image-20250526160718723.png)
#### 配置管理
**开发环境配置示例**
1. 基础配置:
```yaml
order:
timeout: 1min
auto-confirm: 1h
```
2. 数据库配置:
```yaml
order:
db-url: jdbc:mysql://dev-db:3306/order_dev
```
![配置管理界面](./images/image-20250526160842062.png)
#### 命名空间克隆
通过克隆功能快速创建相似环境的命名空间:
![命名空间克隆界面](./images/image-20250526161104319.png)
### 动态环境配置
#### Spring Boot 配置方案
**基础配置**
```yaml
server:
port: 8000
spring:
application:
name: service-order
profiles:
active: dev
cloud:
nacos:
server-addr: ${NACOS_HOST:192.168.95.135}:8848
config:
namespace: ${spring.profiles.active:dev} # 动态匹配当前profile
group: DEFAULT_GROUP
```
**多环境配置加载**
```yaml
spring:
config:
import:
- optional:nacos:service-order.yml
- optional:nacos:common.yml?group=order
- optional:nacos:database.yml?group=order
```
> [!NOTE]
> 使用`optional:`前缀可避免配置不存在时启动失败
### 配置读取实现
**配置类**
```java
@Configuration
@ConfigurationProperties(prefix = "order")
@Getter
@Setter
public class OrderProperties {
private String timeout;
private String autoConfirm;
private String dbUrl; // 自动映射db-url
}
```
**REST接口**
```java
@RestController
@RequestMapping("/api/order")
@RequiredArgsConstructor
public class OrderController {
private final OrderProperties orderProperties;
@GetMapping("/config")
public Map<String, String> getConfig() {
return Map.of(
"timeout", orderProperties.getTimeout(),
"autoConfirm", orderProperties.getAutoConfirm(),
"dbUrl", orderProperties.getDbUrl()
);
}
}
```
### 按需加载策略
#### 多环境差异化配置
```yaml
spring:
profiles:
active: prod # 可通过启动参数覆盖
---
# 生产环境配置
spring:
config:
import:
- nacos:service-order-prod.yml
- nacos:common-prod.yml?group=order
activate:
on-profile: prod
---
# 开发环境配置
spring:
config:
import:
- nacos:service-order-dev.yml
- nacos:database-dev.yml?group=order
activate:
on-profile: dev
---
# 测试环境配置
spring:
config:
import:
- nacos:service-order-test.yml
- nacos:database-test.yml?group=order
activate:
on-profile: test
```