JavaWeb_SpringCloud微服务_Day2-Nacos, Feign, GateWay
nacos
统一配置管理
- 依赖
<!-- nacos配置管理依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
- bootstrap.yml
spring: application: name: userservice # 服务名称 profiles: active: dev # 开发环境 cloud: nacos: server-addr: localhost:8848 # Nacos地址 # discovery: # # cluster-name: SH # namespace: 84c5c3c0-8065-468b-8708-5f699f392f67 # 命名空间 config: namespace: 84c5c3c0-8065-468b-8708-5f699f392f67 # 命名空间 file-extension: yaml # 文件后缀名
- 拉取配置
@Value("${pattern.dateformat}") private String dateformat; @GetMapping("/now") public String now() { return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat)); }
- 总结
- nacos配置读取在本地配置读取之前, 所以需要引入bootstrap.yml写入nacos配置, bootstrap.yml的优先级很高
- 服务名称+开发环境+文件后缀名 = 读取文件id
配置热更新
- 通过
@Value
注解注入, 结合@RefreshScope
来刷新@RefreshScope public class UserController { @Value("${pattern.dateformat}") private String dateformat; }
- 通过@ConfigurationProperties注入, 自动刷新
@Component @ConfigurationProperties(prefix = "pattern") public class PatternProperties { private String dateformat; }
- 注意事项
- 不是所有的配置都式和放到配置中心, 维护起来比较麻烦
- 建议将一些关键参数, 需要运行时调整的参数放到nacos配置中心, 一般都是自定义配置
多环境配置共享
微服务会从nacos读取的配置文件
- [服务名]-[spring.profile.active].yaml, 环境配置
- [服务名].yaml, 默认配置, 多环境共享
优先级
[服务名]-[环境].yaml > [服务名].yaml > 本地配置
feign
快速入门
- 引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
- 启动类添加注解开启Feign
@EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
- 编写Feign客户端
@FeignClient("userservice") public interface UserClient { @GetMapping("/user/{id}") User findById(@PathVariable Long id); }
- 用Feign客户端代替RestTemplate
@Autowired private UserClient userClient; public Order queryOrderById(Long orderId) { // 1.查询订单 Order order = orderMapper.findById(orderId); // 2. 利用Feign发起http请求, 查询用户 User user = userClient.findById(order.getUserId()); // 3 封装user到order order.setUser(user); // 4.返回 return order; }
自定义配置
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
- 通过配置文件
feign: client: config: default: # default是全局配置 loggerLevel: FULL # 日志级别 # userservice: # 单个微服务的配置 # loggerLevel: FULL # 日志级别
- 通过代码
// 声明Bean
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLogLevel()
{
return Logger.Level.BASIC;
}
}
// 在启动类添加注解
// 全局配置
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
// 局部配置
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class)
性能优化
Feign底层的客户端实现
- URLConnection: 默认实现, 不支持连接池
- Apache HttpClient: 支持连接池
- OKHttp: 支持连接池
优化Feign性能主要包括
- 使用连接池代替默认的URLConnection
- 日志级别, 最好用basic或none
连接池配置
- 依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
- 配置
feign: httpclient: enabled: true # 开启feign对HttpClient的支持 max-connections: 200 # 最大的连接数 max-connections-per-route: 50 # 每个路径的最大连接数
最佳实践
- 方式一(继承): 给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
- 服务紧耦合
- 父接口参数列表中的映射不会被继承
- 方式二(抽取): 将FeignClient抽取为独立模块, 并且把接口有关的pojo, 默认的feign配置都放到这个模块中, 提供给消费者使用
- 首先创建一个module,命名为feign-api,然后引入feign的starter依赖
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
- 将order-service中编UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
- 在order-service中引入feign-api的依赖
<dependency> <groupId>cn.itcast.demo</groupId> <artifactId>feign-api</artifactId> <version>1.0</version> </dependency>
- 修改order-service中的所有与上述三个组件有关的import部分,改成导入feign-api中的包
- 导入FeignClient(启动类)
// 方式一: 指定FeignClient所在包 @EnableFeignClients(basePackages = "cn.itcast.feign.clients") // 方式二: 指定FeignClient字节码 @EnableFeignClients(clients = {UserClient.class})
- 首先创建一个module,命名为feign-api,然后引入feign的starter依赖
GateWay
介绍
网关的作用
- 对用户请求做身份认证, 权限校验
- 将用户请求路由到微服务, 并实现负载均衡
- 对用户请求做限流
SpringCloud中网关的实现
- GateWay
- Zuul
注: Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。
快速入门
- 创建module, 引入SpringCloudGateWay依赖和nacos服务发现依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>
- application.yml
server: port: 10010 spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: # 网关路由配置 - id: user-service # 路由id, 自定义, 唯一即可 # uri: http://127.0.0.1:8081 # 固定地址 uri: lb://userservice # lb就是负载均衡, 后面跟服务名称 predicates: # 路由断言, 也就是判断请求是否符合路由规则的条件 - Path=/user/** # 这个是按照路径匹配, 只要以/user/开头就符合要求 - id: order-service uri: lb://orderserver predicates: - Path=/order/**
路由断言工厂
Spring提供了11种Predi工厂
名称 | 说明 |
---|---|
After | 是某个时间点后的请求 |
Before | 是某个时间点之前的请求 |
Between | 是某两个时间点之前的请求 |
Cookie | 请求必须包含某些cookie |
Header | 请求必须包含某些header |
Host | 请求必须是访问某个host (域名) |
Method | 请求方式必须是指定方式 |
Path | 请求路径必须符合指定规则 |
Query | 请求参数必须包含指定参数 |
RemoteAddr | 请求者的ip必须是指定范围 |
Weight | 权重处理 |
过滤器
- 配置
spring: cloud: gateway: routes: # 网关路由配置 filters: # 过滤器 - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
- 代码
public User queryById(@PathVariable("id") Long id, @RequestHeader(value = "Truth", required = false) String truth) { System.out.println("truth: "+truth); return userService.queryById(id); }
- 对所有路由生效的配置
spring: cloud: gateway: default-filters: # 默认过滤器, 会对所有的路由器请求都生效 - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
全局过滤器
@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2. 获取authorization参数
String auth = params.getFirst("authorization");
// 3. 校验
if("admin".equals(auth)){
// 放行
return chain.filter(exchange);
}
// 4. 拦截
// 4.1 禁止访问
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2 结束处理
return exchange.getResponse().setComplete();
}
}
过滤器执行顺序
- order值越小, 优先级越高
- order值一样时, defaultFilter>局部的路由过滤器>全局过滤器
跨域
跨域:域名不一致就是跨域,主要包括
- 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和miaosha,jd.com
- 域名相同,端口不同: localhost:8080和localhost:8081
- 跨域问题: 浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题
- 解决方案: CORS
配置
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
- "http://www.leyou.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
来源
黑马程序员. SpringCloud微服务