SpringCloud复习02

此篇文章是在B站学习黑马SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式时所做的笔记。

1.Nacos配置管理

Nacos不仅可以做注册中心,还可以配置管理服务。

1.1 统一配置管理

当微服务的部署越来越多时,我们微服务配置就会越来越多,如果不想逐个管理起来,我们就可以通过Nacos的配置管理服务把它们集中起来管理。

1.1.1在Nacos中管理配置的步骤:

1.1.2 微服务怎么获取这些配置信息?

微服务获取配置信息流程如下:

微服务获取配置信息流程图

nacos中的配置文件会和application.yml配置文件合并,这时候nacos的地址就要在读取nacos配置文件之前被读取到,所有就要使用bootstrap.yml(优先级高于application.yml和nacos里的配置文件)这个配置文件。

配置步骤:

  1. 引入Nacos配置管理客户端依赖。
<!--nacos配置管理依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  1. 在服务消费者的resource目录添加一个bootstrap.yml文件,这个文件是引导文件。
# 需要去把在application里面的重复的配置删除
spring:
  application:
    name: userservice # 服务名称
  profiles:
    active: dev #开发环境,这里是dev
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
        file-extension: yaml # 文件后缀名
  1. 测试,用在Nacos管理配置中的pattern.dateformat属性注入到UserController中做测试:
@RestController
@RequestMapping("/user")
public class UserController {

    // 注入nacos中的配置属性
    @Value("${patten.dateformat}")
    private String dateformat;


    // 编写controller,通过日期格式化器来格式化现在时间并返回
    @GetMapping("now")
    public String now(){
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern(dateformat));
    }
}

1.2 配置热更新

我们要实现修改Nacos配置后,微服务无需重启就可以让配置生效,这就是配置热更新。

方式一:在@Value注入的变量所在类上加上@RefreshScope

@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {

    // 注入nacos中的配置属性
    @Value("${patten.dateformat}")
    private String dateformat;
}

方式二:使用@ConfigurationProperties注解

@Data
@Component
@ConfigurationProperties(prefix = "patten")
public class PatternProperties {

    private String dateformat;

}


@RestController
@RequestMapping("/user")
@RefreshScope
public class UserController {
    @Autowired
    private PatternProperties properties;
}


小结:Nacos配置更改后,要想使用热更新,有两种方式:① 通过@Value注入结合@RefreshScope一起使用;② 通过@ConfigurationProperties("prefix=“xx”)注入。

注意

  • 不是所有的配置都适合放到配置中心,不然维护起来会比较麻烦。
  • 建议将一些关键参数,需要运行时调整的参数放到nacos配置中心,一般都是自定义的配置。

1.3 配置共享

微服务启动时会读取nacos的多个配置文件,列如:

  • [spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml

  • [spring.application.name].yaml,例如:userservice.yaml

[spring.application.name].yaml 没有环境,所有它是可以被多个环境共享的。

1.3.1 共享环境添加

1.3.2 多种配置的优先级

优先级

[服务名]-[环境].yaml > [服务名称].yaml > 本地配置

1.4 搭建Nacos集群

2.Http客户端Feign

我们先来看看利用RestTemplate发起远程调用的代码和Feign发起的远程调用代码对比一下。

//RestTemplate发起远程调用
String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);

//Feign发起的远程调用
User user = userClient.findById(order.getUserId());

RestTemplate存在的缺点:① 代码可读性差,编程体验不统一;② 参数复杂URL难以维护。

2.1 Feign代替RestTemplate

Feign的使用步骤:

  1. 引入依赖:
        <!--引入Feign远程调用依赖代替RestTemplate远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  1. 在order-service的启动类添加@EnableFeignClients开启Feign功能
@EnableFeignClients   // 添加这个注解开启Feign功能
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
  1. 编写Feign客户端
@FeignClient("userservice")  // 里面的值是被远程调用的服务名称
public interface UserClient {

    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);

}
/*
主要是基于SpringMVC的注解来声明远程调用的信息:
服务名称:userservice
请求方式:GET
请求路径:/user/{id}
请求参数:Long id
返回值类型:User
*/
  1. 用Feign代替RestTemplate

小结:Feign的使用步骤:① 引入依赖,② 添加@EnableFeignClients注解,③ 编写FeignClient接口,④ 使用FeignClient中定义的方法代替RestTemplate

2.2 自定义配置

Feign运行自定义配置来覆盖默认配置,可以修改的配置如下:

类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder响应结果的解析器http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder请求参数编码将请求参数编码,便于通过http请求发送
feign. Contract支持的注解格式默认是SpringMVC的注解
feign. Retryer失败重试机制请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

配置Feign日志的两种方式:

方式一:配置文件方式

  1. 全局生效:
feign:  
  client:
    config: 
      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #  日志级别 
  1. 局部生效:
feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

而日志的级别分为四种:

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

方式二:java代码方式,需要先声明一个@Bean

  1. 先创建一个类,在声明一个@bean。
public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}
  1. 如果是全局配置,则把它放到@EnableFeignClients这个注解中。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 
  1. 如果是局部配置,则把它放到@FeignClient这个注解中。
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

2.3 Feign使用优化

Feign底层的客户端实现:

  • URLConnection:默认实现,不支持连接池(所以影响性能)

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

优化Feign的性能:

① 使用连接池代替默认的URLConnection;② 日志级别,最好用basic或none。

Feign的性能优化-连接池配置

  1. 在order-service的pom文件中引入Apache的HttpClient依赖。
        <!--httpClient的依赖 -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
        </dependency>
  1. 配置连接池,在order-service的application.yml中添加配置。
feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

2.4 最佳实践

方式一(继承):给服务消费者的FeignClient和服务提供者的XxxController继承同一个接口。

方式二(抽取):将FeignClient、POJO、Feign的默认配置都封装到一个模块当中,提供给服务消费者使用。

2.4.1 使用方式二抽取

  1. 创建一个模块用来保存要抽取的类和引入Feign远程调用依赖

        <!--引入Feign远程调用依赖代替RestTemplate远程调用-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
  1. 在要发起远程调用的模块引入feign-api依赖,在把抽取出去的类删除。

  2. 解决扫描包问题。

方式一:指定feign应该扫描的包

@EnableFeignClients(basePackages = "cn.itcast.feign.client")

方式二:指定需要加载的Client接口

@EnableFeignClients(clients = {UserClient.class})

3.统一网关Gateway

3.1 为什么需要网关

我们的业务不全是对外公开的,有些业务只能是公司的工作人员去访问,这时候就需要网关来统一入口。

网关功能:① 对用户请求做身份认证、权限校验;② 将用户请求路由到微服务,并实现负载均衡;③ 对用户请求做限流。

通俗的来说网关就是用来保护微服务的。

在SpringCloud中网关的实现有两种:① gateway;② zuul。

gateway是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能;Zuul是基于Servlet的实现,属于阻塞式编程。

3.2 gateway快速入门

实现网关基本路由功能的步骤:

  1. 创建SpringBoot模块,引入nacos客户端依赖和网关依赖。
    <!-- nacos客户端依赖包 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

	<!--引入gateway网关依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
  1. 编写启动类

  2. 编写基础配置和路由功能

server:
  port: 10010   # 网关端口
spring:
  application:
    name: gateway   # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848   # nacos地址
    gateway:
      routes:     # 网关路由配置
        - id: user-service   # 路由id,自定义,只要唯一即可
        # uri:http://127.0.0.1:8081 # 路由的目标地址http就是固定地址
          uri: lb://userservice   #路由的目标地址lb就是负载均衡,后面跟服务名称
          predicates:   # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/**   # 这个是按照路径匹配,只要以/user/开头就符合要求
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**
  1. 重启测试

重启网关,访问http://localhost:10010/user/1时,符合/user/**规则,请求会转发到uri:http://userservice/user/1。

  1. 网关路由流程图

3.3 断言工厂

3.3.1 路由断言工厂Route Predicate Factory

我们在配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件

例如Path=/user/**是按照路径匹配,这个规则是由

org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory类来

Spring提供了11种基本的Predicate工厂:

名称说明示例
After是某个时间点后的请求- After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before是某个时间点之前的请求- Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between是某两个时间点之前的请求- Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie请求必须包含某些cookie- Cookie=chocolate, ch.p
Header请求必须包含某些header- Header=X-Request-Id, \d+
Host请求必须是访问某个host(域名)- Host=.somehost.org,.anotherhost.org
Method请求方式必须是指定方式- Method=GET,POST
Path请求路径必须符合指定规则- Path=/red/{segment},/blue/**
Query请求参数必须包含指定参数- Query=name, Jack或者- Query=name
RemoteAddr请求者的ip必须是指定范围- RemoteAddr=192.168.1.1/24
Weight权重处理

我们可以在SpringCloud官网查询这11个断言工厂的用法,官网:https://spring.io/projects/spring-cloud

下面是断言工厂After用法。

server:
  port: 10010   # 网关端口
spring:
  application:
    name: gateway   # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848   # nacos地址
    gateway:
      routes:     # 网关路由配置
        - id: user-service   # 路由id,自定义,只要唯一即可
        # uri:http://127.0.0.1:8081 # 路由的目标地址http就是固定地址
          uri: lb://userservice   #路由的目标地址lb就是负载均衡,后面跟服务名称
          predicates:   # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/**   # 这个是按照路径匹配,只要以/user/开头就符合要求
            - After=2037-01-20T17:42:47.789-07:00[America/Denver]   # 断言工厂After用法
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**

3.4 过滤器工厂

3.4.1 GatewayFilter路由过滤器

GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理:

Spring提供了31种不同的路由过滤器工厂,例如:

名称说明
AddRequestHeader给当前请求添加一个请求头
RemoveRequestHeader移除请求中的一个请求头
AddResponseHeader给响应结果中添加一个响应头
RemoveResponseHeader从响应结果中移除有一个响应头
RequestRateLimiter限制请求的流量

案例:给所有进入userservice的请求添加一个请求头。

实现方式:在gateway中修改application.yml文件,给userservice的路由添加过滤器。

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/** 
        filters: # 过滤器
        - AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头

默认过滤器:如果要对所有的路由都生效,则可以将过滤器工厂写到default下。格式如下:

spring:
  cloud:
    gateway:
      routes:
      - id: user-service 
        uri: lb://userservice 
        predicates: 
        - Path=/user/**
      default-filters: # 默认过滤项
      - AddRequestHeader=Truth, Itcast is freaking awesome! 

3.5 全局过滤器

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter效果一样;区别在于GatewayFilter是通过application.yml配置的,处理逻辑也是固定的;而GlobalFilter的逻辑需要自己写代码实现。

实现方式是实现GlobalFilter接口。

public interface GlobalFilter {
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

自定义过滤器步骤:

  1. 实现GlobalFilter接口。
  2. 在实现GlobalFilter接口的类上面需要添加两个注解:@Component@Order(这个注解是执行这个过滤器的顺序值越小优先级越高,因为以后可能会定义很多个过滤器)。

3.6 过滤器的执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。

路由过滤器、defaultFilter、全局过滤器的执行顺序:

  1. order值越小,优先级越高

  2. 当order值一样时,顺序是defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器

3.7 跨域问题

跨域:域名不相同;域名相同,端口号不相同。

跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题。

3.7.1 解决跨域问题

在gateway服务的application.yml文件中配置下的内容:

spring:
  cloud:
    gateway:
      # 。。。
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

SpringCloud复习01
SpringCloud复习03

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@小九九

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值