1.服务注册
1.1 在父工程中添加spring-cloud-alibaba的管理依赖.
-
在原有 spring-Cloud-dependencies的基础上加上spring-cloud-alibaba的管理依赖:
<!--spring cloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
注册服务的pom文件
<!--spring boot parent -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--坐标-->
<groupId>com.nanoswarm</groupId>
<artifactId>nacosparent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacosparent</name>
<description>Demo project for Spring Boot</description>
<!--打包方式-->
<packaging>pom</packaging>
<!--版本配置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR10</spring-cloud.version>
<mysql.version>5.1.47</mysql.version>
<mybatis.version>2.1.1</mybatis.version>
</properties>
<!--版本溯源-->
<dependencyManagement>
<dependencies>
<!--spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--spring cloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--依赖-->
<dependencies>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.2 在注册的服务中心添加依赖
<!--nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1.3 yml
spring:
cloud:
nacos:
server-addr: localhost:8848
2.服务多级存储模型
-
以机房划分集群,分成三级.
修改服务所在的集群
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ #配置集群名称,也就是机房位置,例如 HZ 杭州
3.NacosRule负载均衡策略
-
在服务设置了集群之后,不会走同集群优先访问的负载均衡策略,需要对Ribbon负载均衡策略进行配置.
#NacosRule负载均衡
user-service:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
NacosRule : 优先访问同集群(HZ),在同集群内进行随机负载均衡策略.
4.Nacos服务实例权重设置
-
在Nacos控制台可以设置实例的权重值,首先选中实例后面的编辑按钮
将权重设置为0.1,测试可以发现8081被访问到的频率大大降低
-
将权重设置为0,则完全不会被访问,可以用于灰度发布
5.Nacos 环境隔离
-
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
namespace: namespace-name # 填命名空间的Id 可能用于项目环境 : 开发 , 测试 , 生产 环境
6.Eureka 与 Nacos
-
向Nacos注册的实例默认为临时实例.
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
ephemeral: false //false : 永久实例
7.配置管理
7.1 Nacos 创建配置管理
-
DataId : 服务名-profile.文件后缀
7.2 微服务配置拉取
服务拉取配置信息的优先级 : bootstrap.yml-远程配置中心-application.yml.
所以将Nacos配置管理的DataId的组成部分放入到Bootstap.yml中,以读取配置文件中的信息,再与application.yml做合并.
-
添加Nacos配置依赖
<!--nacos config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
编写Bootstrap.yml
spring:
application:
name: user-service
profiles:
active: dev
cloud:
nacos:
server-addr: localhost:8848
config:
file-extension: yaml
7.3 配置热更新
-
创建配置文件
-
使用@ConfigurationProperties(prefix = "pattern")
-
@Component
@Data
@ConfigurationProperties(prefix = "pattern") //远程配置中心
public class PatternConfig {
private String dateformat;
private String envConfig;
}
7.4 多环境配置共享
开发 , 生产 , 测试的公用配置
-
微服务启动时会从nacos读取多个配置文件
-
[spring.application.name]-[spring.profiles.active].yaml,例如:user-service-dev.yaml
-
[spring.application.name].yaml,例如:user-service.yaml
-
-
无论profile如何变化,[服务名].yaml这个文件一定会加载,因此多环境共享配置可以写入这个文件
-
配置服务环境
8.Feign
1.使用
-
引入依赖
<!--openFiegn-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在启动类添加注解 @EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class OrderServiceNacosApp {
public static void main(String[] args) {
SpringApplication.run(OrderServiceNacosApp.class,args);
}
}
编写声明式客户端
@FeignClient("user-service")
public interface UserClient {
@GetMapping("/user/one/{id}")
String queryUserById(@PathVariable("id") Long id);
}
远程调用
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private UserClient userClient;
@Override
public String selectOrderUserInfo(Long id) {
String orderInfo = "this is the information of order where id = " + id+" ";
String userInfo = userClient.queryUserById(id);
return orderInfo + userInfo;
}
}
9.Feign自定义配置
-
Feign运行自定义配置来覆盖默认配置,通常修改Feign的配置级别
#feign 日志级别
feign:
client:
config:
user-service:
loggerLevel: NONE
如果想看到日志则必须把SpringBoot的配置级别开启(进行配置,不能不写)
#日志级别
logging:
level:
com.nanoswarm: debug
10.性能优化
-
Feign底层可客户端实现
-
URLConnection : 默认实现,不支持连接池 //JDK自带
-
Apache HttpClient : 支持连接池
-
OKHttp : 支持连接池
-
-
因此优化Feign的性能主要包括
-
使用连接池替代默认的URLConnection
-
日志级别 , 最好使用basic或none
-
10.1 引入依赖
<!--httpClient-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
10.2 添加配置
#feign 日志级别
feign:
client:
config:
user-service:
loggerLevel: NONE
httpclient:
enabled: true
max-connections: 200
max-connections-per-route: 50
11.网关
-
网关功能:
-
身份认证和权限校验
-
服务路由,负载均衡
-
请求限流
-
11.1 搭建
-
pom
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
application.yml
spring:
application:
name: gateway-service
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: userServiceRounte #路由id,自定义,唯一即可
uri: lb://user-service #路由目标地址
predicates: #路由断言,判断请求是否符合路由规则条件
- Path=/user/**
11.2 路由断言工厂
在路由配置的predicates中不仅仅可以配 - Path 还可以配很多其他东西.详见 Spring Cloud Gateway
predicates: #路由断言,判断请求是否符合路由规则条件
- Path=/user/**
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanaghai] //在2017年之后才能访问
11.3 路由过滤器
-
Spring提供了31种不同的路由过滤器工厂,详见如下.
spring:
application:
name: gateway-service
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: userServiceRounte #路由id,自定义,唯一即可
uri: lb://user-service #路由目标地址
predicates: #路由断言,判断请求是否符合路由规则条件
- Path=/user/**
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanaghai]
filter:
- AddRequestHeader=AddRequestHeaderKey, AddRequestHeaderValue
获取相应头 (在user-service里面接收请求头)
@GetMapping("/one/{id}")
public String queryOrderUserInfoByOrderId(@PathVariable("id") Long id,
@RequestHeader("AddRequestHeaderKey") String AddRequestHeaderValue ){
System.out.println("由默认过滤器添加的请求头为 "+AddRequestHeaderValue);
return orderService.selectOrderUserInfo(id);
}
默认过滤器
在全局加过滤 : 对所有请求都生效
default-filters:
- AddRequestHeader=AddRequestHeaderKey,AddRequestHeaderValue
spring:
application:
name: gateway-service
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: userServiceRounte #路由id,自定义,唯一即可
uri: lb://user-service #路由目标地址
predicates: #路由断言,判断请求是否符合路由规则条件
- Path=/user/**
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanaghai]
- id: orderServiceRounte
uri: lb://order-service
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=AddRequestHeaderKey,AddRequestHeaderValue
11.4 全局过滤器
-
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。 区别在于GatewayFilter通过配置定义,处理逻辑是固定的。而GlobalFilter的逻辑需要自己写代码实现。 定义方式是实现GlobalFilter接口。
public interface GlobalFilter{
/** 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
* @param exchange 请求上下文,里面可以获取Request、Response等信息
* @param chain 用来把请求委托给下一个过滤器
* @return {@code Mono<Void>} 返回标示当前过滤器业务结束 */
Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}
案例 :
-
需求:定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:
-
参数中是否有password
-
password参数值是否为123456
-
-
如果同时满足则放行,否则拦截
@Order(-1)
@Component
public class authorizedFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
String password = queryParams.getFirst("password");
if("123456".equals(password)){
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
}
11.5 过滤器链执行顺序
-
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
-
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
-
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
.
-
当过滤器的order值一样时,会按照以下顺序执行,也就是说排序先看@Order()的值,如果值一样......
-
默认过滤器 default-filters
-
|
-
路由过滤器 filter
-
|
-
全局过滤器 GlobalFilter
11.6 跨域
-
跨域配置
globalcors:
add-to-simple-url-handler-mapping: true #是否允许option请求
cors-configurations:
'[/**]':
allowedOrigins:
- "http:localhost:8090"
allowedMethod:
- "GET"
- "DELETE"
- "POST"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" #允许在请求中携带的头信息
allowedCredentials: true #是否允许携带cookie
maxAge: 360000 #这次跨域检测的有效期
yaml
server:
port: 10010
spring:
application:
name: gateway-service
cloud:
nacos:
server-addr: localhost:8848
gateway:
routes:
- id: userServiceRounte #路由id,自定义,唯一即可
uri: lb://user-service #路由目标地址
predicates: #路由断言,判断请求是否符合路由规则条件
- Path=/user/**
- After=2017-01-20T17:42:47.789-07:00[Asia/Shanaghai]
- id: orderServiceRounte
uri: lb://order-service
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=AddRequestHeaderKey,AddRequestHeaderValue
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
allowedOrigins:
- "http:localhost:8090"
allowedMethod:
- "GET"
- "DELETE"
- "POST"
- "PUT"
- "OPTIONS"
allowedHeaders: "*"
allowedCredentials: true
maxAge: 360000 #这次跨域检测的有效期,不用每次ajax每次都询问服务器一次