在我们使用之前的RestTemplate远程调用的时候,是这样的:

 它存在一个问题就是:

•代码可读性差,编程体验不统一

•参数复杂URL难以维

Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign

其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。

Feign: 代替消费者向提供者发送请求,并接受返回结果
    openfeign: SpringCloud官方提供的组件 

Feign使用步骤 

第一步:导入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

 第二步:在启动类上添加注解开启Feign功能

 

创建Feign的客户端:

@FeignClient("userservice")
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    User queryById(@PathVariable("id") Long id);
}

也就是代表访问的地址就是:http://userservice/user/id

也就是说:@FeignClient("userservice")+GetMapping("/user/{id}")里面的内容拼成的一个链接
然后我们再使用的时候就可以直接注入这个类调用这一层的方法就可以完成远程调用了

Feign自定义配置 

 

配置文件方式:
基于配置文件修改feign的日志级别可以针对单个服务:
如果在这个项目中配置了,那么当我们只有远程调用userservice的都会促发这个配置。

feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

将那个userservice去掉也就可以根据配置全局配置,也就是说如果在这个项目中配置了,那么当我们远程调用所有的都会促发这个配置。
 

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

 使用Java代码实现自定义配置

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
// 配置类的方式开启全局日志记录
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) // 开启feign客户端的支持
@EnableFeignClients // 开启feign客户端的支持
public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

   //......
}

如果是局部生效,则把它放到对应的@FeignClient这个注解中:

// 指定服务日志配置
@FeignClient(value = "userservice",configuration = DefaultFeignConfiguration.class)
@FeignClient(value = "userservice")
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    User queryById(@PathVariable("id") Long id);
}

 Feign的使用优化:

 Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:

•URLConnection:默认实现,不支持连接池

•Apache HttpClient :支持连接池

•OKHttp:支持连接池

其实也就是Feign远程调用还是使用的RestTmplate,但是默认使用的时URLConnection,是不支持连接池的,那么我们想要使用连接池的话就需要使用HttpClient和OkHttp,这两个支持连接池


Feign优化使用步骤 

 引入依赖

<!--httpClient的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

配置连接池
 

feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

注意:我们在导入了HttpClient的依赖以后就会直接将默认的给替换掉,即使不配置那么也会提换掉,个人认为只要导入了HttpClient依赖bean的工厂就有这个实例,那么Feign就会发现有这个实例就会优先调用HttpClirnt,如果有连接词的这个两种都没有导入依赖那么就是没有实例,Feign发现没有实例就会使用默认的

总结,Feign的优化:

  1. 日志级别尽量用basic

2.使用HttpClient或OKHttp代替URLConnection

① 引入feign-httpClient依赖

② 配置文件开启httpClient功能,设置连接池参数

Feign最佳实践 

所谓最近实践,就是使用过程中总结的经验,最好的一种使用方式。

仔细观察可以发现,Feign的客户端与服务提供者的controller代码非常相似:

 

 

 

 

 也就是将feign单独分离出来做成一个jar包依赖导入我们的i项目中。

Gateway网关

Gateway网关是我们服务的守门神,所有微服务的统一入口。

网关的核心功能特性

  • 请求路由

  • 权限控制

  • 限流

也就是说我们的集群全部交给网关调用,我们只需要访问网关就行,网关给我们负载均衡,替我们访问

 

权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。

路由和负载均衡:一切请求都必须先经过gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。

限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大

getaway 网关使用班步骤:

引入依赖:

<!--网关-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--nacos服务发现依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

 编写路由规则:
 

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: localhost:80 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
          filters:
            - AddRequestHeader=Truth, Itcast is freaking awesome!   # 给这个请求添加请求头后面是信息,请求头的标签为Truth   内容为:Itcast is freaking awesome!
        - id: order-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://orderservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件   断言有多种,可以去查看
            - Path= /** # 这个是按照路径匹配,这个是匹配全部
      default-filters: # 默认过滤项
        #也就是说,比如上边的id为user-service他有指定的请求头,但是他们的请求头的key是一样的,那么就是用他自己的,但是下边的order-service没有指定,那么就用默认的
        - AddRequestHeader=Truth, Itcast is freaking awesomedefault!
      # 。。。
      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 # 这次跨域检测的有效期

上边有两个路由:

一个是

- Path=/user/**

还有一个是

- Path= /**

当第二个的放在第一个的位置时,那么我们不管访问什么都会出现一个问题,就是第二个无论什么路径都会满足,这时候就不会进入第一个- Path=/user/**,即使携带user的也会被拦击,这个如果有共同路径时要考虑先后顺序

断言工厂 

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

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

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

处理的,像这样的断言工厂在SpringCloudGateway还有十几个

 

 路由过滤器

 

 

过滤器的作用是什么?

① 对路由的请求或响应做加工处理,比如添加请求头

② 配置在路由下的过滤器只对当前路由的请求生效

defaultFilters的作用是什么?

① 对所有路由都生效的过滤器

全局过滤器

网关提供了31种,但每一种过滤器的作用都是固定的。如果我们希望拦截请求,做自己的业务逻辑则没办法实现。  

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

定义方式是实现GlobalFilter接口。

 代码实现

@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、GlobalFilter

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

 

排序的规则是什么呢?

  • 每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前

  • GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定

  • 路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。

  • 当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

跨域问题

 也就是说一个网页如果他的内容同时出现了来自两个端口的,那么就是跨域

协议,域名,端口三者中任意一者不同即为跨域

跨域:域名不一致就是跨域,主要包括:

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

解决方案:CORS,这个以前应该学习过,这里不再赘述了。不知道的小伙伴可以查看跨域资源共享 CORS 详解 - 阮一峰的网络日志

解决跨域问题 

 在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 # 这次跨域检测的有效期

总结:

Nacos组件:
    概述: alibaba提供的一个组件
    作用: 
        Nacos注册中心
        Nacos配置中心
    注意: nacos注册中心和配置中心各有各的依赖,用哪个导哪个
    安装: 安装nacos,并启动
          startup.cmd -m standalone
Nacos注册中心:
    问题由来:
        两个微服务远程调用时存在硬编码问题
        如果保存多个微服务的地址
        如何监控微服务的健康状态
    作用:
        ...
          内容:
            namespace:
                group:
                    XxxService:
            异地容灾:
                将一个服务下的多个实例,存放在不同的地区,形成不同的集群
                微服务在调用时,优先本集群(就近原则)
    使用步骤:
        1.导入注册中心依赖
        2.配置nacos的地址
        配置微服务集群时,需要设置优先本集群
Nacos配置中心
    问题由来:
        在一个微服务集群中,每一个微服务内都可能存在相同的配置,修改起来不方便
    作用:
        将配置文件从微服务中分离,存放在nacos中
        微服务启动时,从nacos中加载配置文件,并使用配置文件中的配置
        配置文件的热更新: 2种
        配置文件中的多种编写方式:
            服务名-环境.yaml > 服务名.yaml > application.yaml
            在SpringBoot中外部配置(指定配置大于默认配置) 大于 内部配置
    使用步骤:
        0.在nacos中编写配置文件
        1.导入注册中心依赖
        2.配置nacos
            在bootstrap.yml文件中配置nacos配置中心的相关信息
       
    

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐