# cloud-demo
**Repository Path**: li-yakunlixiaobai/cloud-demo
## Basic Information
- **Project Name**: cloud-demo
- **Description**: 小白学SpringCloud
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 8
- **Created**: 2024-10-08
- **Last Updated**: 2024-10-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 第一课 Nacos
## Nacos服务注册
### 版本
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
### 父级项目
```xml
org.springframework.cloud
spring-cloud-dependencies
2020.0.1
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
2021.1
pom
import
org.projectlombok
lombok
true
org.springframework.cloud
spring-cloud-starter-bootstrap
```
### 服务注册
```xml
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-web
```
```java
@EnableDiscoveryClient
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
```
### 错误解决
```
java.lang.IllegalArgumentException: Param 'serviceName' is illegal, serviceName is blank
```
加上依赖:
```xml
org.springframework.cloud
spring-cloud-starter-bootstrap
```
### 启动微服务
> nacos registry, DEFAULT_GROUP service-order 192.168.3.39:8000 register finished
```
-Dserver.port=8002
```
配置多个启动方式,观察nacos注册中心面板的变化
## Nacos服务治理
> java.net.UnknownHostException: service-store
在微服务调用的时候出现的,意思是找不到服务
添加loadBalance:
```xml
org.springframework.cloud
spring-cloud-starter-loadbalancer
```
注册 RestTemplate:
```java
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
```
注册失败错误
> com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance after all servers
重启nacos即可
## Nacos配置中心
> java.lang.IllegalArgumentException: Could not resolve placeholder 'env.ip' in value "${env.ip}"
出现这个错误是因为配置项不存在,那么我们应该给与一个默认值,在配置后面输入一个 : 类似于 ${env:ip:},那么就默认了一个空的字符串

### 基本配置
数据库配置:nacos/conf/application.properties 可以进行数据库的配置
- spring.datasource.platform=mysql
- db.num=1
- db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
- db.user.0=root
- db.password.0=123456
配置完成之后,新建数据库nacos,导入数据文件 nacos-mysql.sql
重启nacos
### 动态刷新
在Controller上面加上@RefreshScope
### dataID格式
`${prefix}-${spring.profiles.active}.${file-extension}`
### Group分组
配置 group,可以将业务类似的配置文件归为一组
### Namespace
namespace里面可以写多个dataID的文件,通过 spring.profile.active 可以区分不同的配置文件
`namespace - group - active` 可以组合出很多种配置
> 注意:在固定的服务里面, prefix必须是当前服务的名称,比如 service-goods,如果prefix改成其他的服务名称,则读取不到配置值
### 共享配置 shared-configs[0]
### extension-configs
## Openfeign远程调用
### 集成方式
```xml
org.springframework.cloud
spring-cloud-starter-openfeign
```
启动类加上注解:`@EnableFeignClients`
FeignConfig
```java
import feign.Feign;
import feign.Logger;
import feign.Retryer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@ConditionalOnClass(Feign.class)
@ConfigurationProperties("feign.httpclient.enable")
@Configuration
public class FeignConfig {
/**
* NONE:默认的,不显示任何日志;
* BASIC:仅记录请求方法、URL、响应状态码及执行时间;
* HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
* FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
*/
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
```
### 重试机制
```java
/**
* 创建重试器 (重试周期(50毫秒),最大重试周期(2000毫秒),最多尝试次数 3次 )
* feign没有采用线性的重试机制而是采用的是一种指数级(乘法)的重试机制 每次重试时间 当前重试时间*= 1.5
* @FeignClient(configuration = FeignConfig.class) 进行配置
*/
@Bean
public Retryer retryer() {
return new Retryer.Default(50, TimeUnit.SECONDS.toMillis(2), 3);
}
```
### 超时时间设置
错误示范:
> Failed to bind properties under 'feign.client.config.connecttimeout' to org.springframework.cloud.openfeign.FeignClientProperties$FeignClientConfiguration:
> Reason: No converter found capable of converting from type [java.lang.Integer] to type [org.springframework.cloud.openfeign.FeignClientProperties$FeignClientConfiguration]
```yaml
feign:
client:
config:
default:
connectTimeout: 5000 #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
readTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间
```
### 日志增强
```java
@Component
@Configuration
public class FeignConfig {
/** NONE:默认的,不显示任何日志;
* BASIC:仅记录请求方法、URL、响应状态码及执行时间;
* HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
* FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。
*/
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
```
### 使用Okhttp
- 支持 HTTP/2 协议。
- 允许连接到同一个主机地址的所有请求,提高请求效率。
- 共享Socket,减少对服务器的请求次数。
- 通过连接池,减少了请求延迟。
- 缓存响应数据来减少重复的网络请求。
- 减少了对数据流量的消耗。
- 自动处理GZip压缩。
```xml
io.github.openfeign
feign-okhttp
```
```yaml
feign:
httpclient:
enabled: false
okhttp:
enabled: true
```
FeignOkhttpConfig
```java
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
public class FeignOkhttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(OkhttpProperties okhttpProperties) {
return new okhttp3.OkHttpClient.Builder()
//设置连接超时
.connectTimeout(okhttpProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
//设置读超时
.readTimeout(okhttpProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
//是否自动重连
.retryOnConnectionFailure(true)
.connectionPool(new ConnectionPool())
.addInterceptor(new OkHttpLogInterceptor())
//构建OkHttpClient对象
.build();
}
}
```
OkhttpProperties
```java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "feign.okhttp")
public class OkhttpProperties {
private Long connectTimeout;
private Long readTimeout;
}
```
OkHttpLogInterceptor
```java
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.IOException;
@Slf4j
public class OkHttpLogInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
//这个chain里面包含了request和response,所以你要什么都可以从这里拿
Request request = chain.request();
long t1 = System.nanoTime();//请求发起的时间
log.info(String.format("发送请求 %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();//收到响应的时间
//注意这里不能直接使用response.body().string()的方式输出日志
//因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一个新的response给应用层处理
ResponseBody responseBody = response.peekBody(1024 * 1024);
log.info(String.format("接收响应: [%s] %n返回json:【%s】 %.1fms%n%s",
response.request().url(),
responseBody.string(),
(t2 - t1) / 1e6d,
response.headers()));
return response;
}
}
```