SpringCloud
分布式:将一种业务拆分成多个子业务部署在多台服务器上
集群:将多台服务器组合在一起提供同一种服务
Sentinel
下载地址:https://github.com/alibaba/Sentinel/releases
文档:https://sentinelguard.io/zh-cn/docs/quick-start.html
https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
运行jar默认是8080端口
指定端口运行
java -jar -Dserver.prot=9090 sentinel-dashboard-1.8.0.jar
sentinel:登录账户密码默认为 sentinel
配置
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server.port=9999
spring.application.name=sentinel
spring.cloud.nacos.server-addr=127.0.0.1:8848
# 开启 sentinel 保护
spring.cloud.sentinel.enabled=true
# 指定 sentinel dashboard web地址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8080
# 指定 sentinel dashboard 组件通信地址端口
spring.cloud.sentinel.transport.port=8719
==注意:得调用接口后sentinel dashboard
面板才会显示接口信息==
QPS:每秒得请求数
RT:每个请求响应时间
Nacos
文档:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html
版本对应关系:https://github.com/alibaba/spring-cloud-alibaba/wiki
问题解决
1,db.num is null 问题
1,mysql 下新建数据库 nacos 字符集 utf8 编码 utf8_general_ci
2,执行 nacos/conf/nacos-mysql.sql 的 sql 脚本文件
3,修改 conf 文件夹下的 application.properties
2,Unable to start web server
解决1,选择 bin 文件夹下的 startup.cmd ,右键编辑,更改集群模式为单机模式 standalone
既 set MODE="cluster" 改为 set MODE="standalone"
解决2,启动命令(standalone代表着单机模式运行,非集群模式):
cmd 中执行 startup.cmd -m standalone
访问
http://127.0.0.1:8848/nacos 默认账户密码 nacos
SpringBoot与Nacos整合
官方文档:https://nacos.io/zh-cn/docs/quick-start-spring-boot.html
官方文档使用的是
nacos-discovery-spring-boot-starter
我这里使用的
spring-cloud-starter-alibaba-nacos-config
1,pom文件
nacos-config这个依赖就相当于SpringCloud Config,nacos-discovery这个依赖就相当于Eureka
<!-- nacos-config这个依赖就相当于SpringCloud -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!-- nacos-discovery这个依赖就相当于Eureka 需要 httpcomponents 包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<!--使用feign调用服务提供者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.7.RELEASE</version>
</dependency>
<!-- 注意版本问题 -->
2,配置中心nacos-config
Resources目录下创建配置文件bootstrap.yml
spring:
application:
# 应用名
name: test-v0710
profiles:
active: dev
cloud:
nacos:
config:
# 配置文件的环境:和nacos中一样定义的一样即可
group: v0710_dev
# 配置文件的格式
file-extension: yaml
# 配置中心的地址
server-addr: 127.0.0.1:8848
# 配置文件prefix
prefix: ${spring.application.name}
#命名空间 :指向哪个空间的,然后取指向空间的 data id 和 group
namespace: v0710
# 动态刷新 然后类上加 @RefreshScope 注解
refresh-enabled: true
配置 Data Id
Nacos中的Data Id= ${spring.application.name} - ${spring.profiles.active} . ${file-extension}
以上配置中的 Data Id 就等于指向 test-dev.yaml
默认 namespace(命名空间)为 public ,
新建一个命名空间为v0710,可在 bootstrap.yml 中指定 namespace: v0710 ,此时这个配置文件就指向自己定义的这个v0710空间中,Data Id 就取这个自建的空间v0710的配置 Data Id
Controller
@RestController
@RefreshScope // 刷新
public class ProvideController {
// 得到的就是上图的配置的 myname
@Value("${myname:null}")
private String name;
@GetMapping("/getName")
public String hi(){
System.err.println(name);
return name;
}
}
浏览器访问 http://localhost:8080/getName 即可得到 nacos 的配置值,随时修改随时得到值
3,注册中心nacos-discovery
bootstrap.yml的nacos节点下增加配置
discovery:
# 服务地址
server-addr: 127.0.0.1:8848
# 组名
group: v0710_dev
# 空间名
namespace: v0710
注:如果配置中心与当前应用的配置文件都配置了相同的key,优先使用配置中心的配置。
在启动类上添加 @EnableDiscoveryClient
注解 开启自动发现服务即可自动添加到nacos的注册中心中
4,Springboot配置文件的加载先后顺序
- 优先加载操作系统层面的配置、命令行
- 由jar包外向jar包内进行寻找,优先查找config目录。
- 优先加载带profile(application-{profile}.yml)的,后加载不带profile(application.yml)的
- 高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
# 以Nacos为例:默认远程配置优先级最高
spring:
cloud:
config:
# 是否允许本地配置覆盖远程配置
allow-override: true
# 是否一切以本地配置为准,默认false
override-none: false
# 系统环境变量或系统属性才能覆盖远程配置文件的配置
# 本地配置文件中配置优先级低于远程配置,默认true
override-system-properties: true
案例
生产者消费者都要注册到nacos
中
1,添加pom
2,添加配置文件
3,添加注解
生产者
提供服务接口供消费者调用
@RestController
@RefreshScope // 刷新
public class ProvideController {
@Value("${myname:null}")
private String name;
@GetMapping("/getName")
public String hi(){
System.err.println(name);
return name;
}
}
消费者
消费调用生产者提供的接口这里采用
fegin
方式调用
启动类添加注解 @EnableFeignClients
创建
@FeignClient(name = "test-v0710") // 服务提供者的 spring.application.name
public interface ConsumerClient {
@GetMapping("/getName")
String ConsumerGetName();
}
调用
@RestController
public class ConsumerController {
@Autowired // 注入创建的 Feign
private ConsumerClient consumerClient;
@GetMapping("/consumer")
public String consumer(){
String name = consumerClient.ConsumerGetName();
System.err.println("consumer=>"+name);
return name;
}}
Gateway 网关
文档,https://docs.spring.io/spring-cloud-gateway/docs/2.2.7.RELEASE/reference/html/
1,原理
Route 路由:gateway的基本构建模块。它由ID、目标URI、断言集合和过滤器集合组成。如果聚合断言结果为真,则匹配到该路由。
Predicate 断言:这是一个Java 8 Function Predicate。输入类型是 Spring Framework
ServerWebExchange
。这允许开发人员可以匹配来自HTTP请求的任何内容,例如Header或参数或请求路径等。Filter 过滤器:这些是使用特定工厂构建的 Spring Framework
GatewayFilter
实例。所以可以在返回请求之前或之后修改请求和响应的内容。
2,使用
导入pom依赖
我这里版本是在父依赖中引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
配置文件
spring:
application:
# 应用名称
name: test-gateway
cloud:
nacos:
discovery:
# 注册中心地址
server-addr: 127.0.0.1:8848
group: v0710_dev
namespace: v0710
# 网关配置
gateway:
discovery:
locator:
# 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
enabled: true
routes:
- id: test-gateway
# 用nacos注册中心的服务名称进行路由
uri: lb://test-v0710
# 匹配规则
predicates:
# **:通配符 ;断言,路径相匹配的进行路由,我这里匹配的是以 getName 开头的接口
- Path=/getName/**
enabled: true
server:
port: 777
3,过滤器
- 全局过滤器与其他2类过滤器(自定义,默认)相比,永远是最后执行的;它的优先级只对其他全局过滤器起作用
- 当默认过滤器与自定义过滤器的优先级一样时,优先出发默认过滤器,然后才是自定义过滤器
filters
下的值可以是 每个GatewayFilterFactory
类的后缀,例如AddRequestHeaderGatewayFilterFactory,则filters
下可配置
AddRequestHeade
过滤
3-1,路径过滤器 Path
Path过滤器可以实现 url 路径重写,通过重写隐藏真实路径提高安全
……………………………等,使用的时候在进行记录
3-2,自定义…Filter
Spring Cloud Gateway 的自定义Filter分为GatewayFilter局部过滤器和GlobalFilter全局过滤器
GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上
GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器
GatewayFilter局部过滤器
如果自定义工厂后缀是以GatewayFilterFactory
结尾,配置文件 filters
下就可以写 GatewayFilterFactory 的前缀
例如:类名 CustomerGatewayFilterFactory – 配置中 filters : Customer
如果自定义工厂后缀不以 GatewayFilterFactory
结尾,配置文件 filters
下就的写具体类的全名
例如:类名 CustomerFilter – 配置中 filters : CustomerFilter
自定义工厂
// @Component
public class CustomerGatewayFilterFactory extends AbstractGatewayFilterFactory {
@Override
public GatewayFilter apply(Object config) {
return new CostomerGatewayFilter();
}
}
也可以把 CostomerGatewayFilter.filter 中逻辑处理放在 CustomerGatewayFilterFactory 的 apply 方法下 若依就是这样实现
自定义局部过滤器
@Slf4j
public class CostomerGatewayFilter implements GatewayFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("自定义局部过滤器:{}====================", "CustomerGatewayFilter");
return chain.filter(exchange);
}
/**
* 值越小,优先级越高,
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
在配置类中将自定义过滤器工厂注册到容器中,当然也可以在自定义过滤器工厂类上加 @Component
注解
@Configuration
public class GatewayConfig {
@Bean
public CustomerGatewayFilterFactory myGatewayFilterFactory() {
return new CustomerGatewayFilterFactory();
}
}
配置文件
spring:
application:
name: service-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes:
- id: service-provider
uri: lb://service-provider
predicates:
- Path=/provider/**
filters:
- Customer # CustomerGatewayFilterFactory 可省略GatewayFilterFactory
https://www.cnblogs.com/zhaoxiangjun/p/13042189.html
https://www.cnblogs.com/westlin/p/10909799.html
GlobalFilter全局过滤器
自定义全局过滤器需要实现 GlobalFilter
和 Ordered
接口
@Slf4j
public class LoginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
String token = request.getHeaders().getFirst("token");
log.info("访问的路径:{}", path);
log.info("token: {}==================", token);
if (token == null) {
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
ResponseData responseData = new ResponseData(401, "请登录");
String res = null;
try {
res = new ObjectMapper().writeValueAsString(responseData);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
DataBuffer wrap = response.bufferFactory().wrap(res.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(wrap));
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
// 值越小 优先级越高 只对全局过滤器起作用,在自定义过滤器后执行
return 0;
}
}
在配置类中注册全局过滤器,这样这个全局过滤器就是全局过滤了
@Configuration
public class GatewayConfig {
@Bean
public GlobalFilter loginFilter() {
return new LoginFilter();
}
}
OpenFeign 远程调用
注意 feign 是内部调用 不走网关
1,feign调用传递请求头
@Configuration
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String authorization = request.getHeader("Authorization");
System.err.println("feign设置header:" + authorization);
requestTemplate.header("Authorization", authorization);
}
}
2,feign调用请求超时时间设置
feign:
client:
config:
qzd-jeepay-pay:#设置为服务名既给该服务单独设置请求超时时间
connectTimeout: 5000 #连接服务时间
readTimeout: 60000 #请求超时时间
3,异步调用获取不到token
解决:
feign接口中添加请求头如下
日夜颠倒头发少 ,单纯好骗恋爱脑 ,会背九九乘法表 ,下雨只会往家跑 ,搭讪只会说你好 ---- 2050781802@qq.com