0
点赞
收藏
分享

微信扫一扫

软考高级系统架构设计师系列之:系统开发基础知识、项目管理、信息安全和网络安全、计算机网络章节选择题详解

西红柿上校 2023-11-02 阅读 44

目录

一、Feign 简介

1、RestTemplate 远程调用中存在的问题

2、定义和使用 Feign 客户端

3、Feign 自定义配置

4、Feign 性能优化

5、Feign 最佳实践

6、Feign 使用问题汇总

二、Gateway 网关简介

1、搭建网关服务

2、路由断言工厂

3、路由的过滤器配置

4、全局过滤器

5、过滤器链执行顺序

6、网关的 cors 跨域配置

7、Gateway 网关使用问题汇总


一、Feign 简介

Feign 是 Netflix 开发的声明式、模板化的 Http 客户端,Feign 可以帮助我们更快捷、优雅地调用Http API。

  • Feign 是在 Ribbon 的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。
  • SpringCloud 对 Feign 进行了增强,使 Feign 支持了 Spring MVC 注解,并整合了 Ribbon 和 Eureka,集成了服务发现和负载均衡,从而让 Feign 的使用更加方便。

1、RestTemplate 远程调用中存在的问题

该远程调用的代码有以下问题:

  • 代码可读性差,编程体验不统一;
  • 参数复杂,URL 难以维护;

2、定义和使用 Feign 客户端

在 SpringCloud 中,使用 Feign 非常简单,创建一个 Interface,并在接口上添加一些注解,代码就完成了。 

(1)引入依赖

  • 给需要远程调用的模块,引入 openfeign 起步依赖;

(2)添加 @EnableFeignClients 注解

  • 给需要远程调用的启动类,添加 @EnableFeignClients 注解;

(3)编写 Feign 客户端

  • 添加 @FeignClient 和 @GetMapping 注解; 

上述注解声明了如下信息:

  • 服务名称:user-service;
  • 请求方式:get;
  • 请求路径:/user/{userId};
  • 请求参数:String userId;
  • 返回值类型:User;

(4)OrderService 远程调用过程

该远程调用过程,就好像平常处理请求的逆过程:

  • OrderService 将 userId 传递给接口中的方法;
  • queryUserById 方法再将参数赋值给请求路径中的占位符 {userId};
  • 然后再通过 get 请求向 UserController 发送请求;
  • 最终获得 User 对象信息;

3、Feign 自定义配置

Spring 虽然帮我们做好了配置,但是也允许我们修改默认配置。

配置 Feign 日志有 2 种方式:

(1)application/bootstrap 配置文件方式

  • 注意:logging.level 属性值需要改为 trace; 

(2)Java 代码方式

4、Feign 性能优化

因此优化 Feign 的性能主要包括:

  • 使用连接池代替默认的 URLConnection;
  • 日志级别,最好用 basic 或 none;(因为开启日志也需要占用较多资源)

下面以 HttpClient 为例子,说明性能优化的步骤:

(1)引入依赖

  • httpClient 已经被 Spring 管理好版本了,不需要指定版本;

(2)修改 application 配置文件

  • 将 feign.httpclient.enabled 的属性值改为 true,表明底层使用 httpclient;

5、Feign 最佳实践

最佳实践是指,企业开发过程中,总结设计缺点,得出的一种相对比较好的使用方式。

我个人更喜欢第二种抽取的方式,下面使用这种方式做一个例子。

(1)创建 feign-api 模块

  • 在 feign-api 模块中引入 feign 的起步依赖;

  • 将消费者模块中对提供者模块所需的 Client、Pojo,以及 Config 类都移动到 feign-api 中;

(2)引入 feign-api 模块

  • 在消费者 order-service 模块中,引入 feign-api 模块;

(3)消除报错

  • 将 order-service 模块内原来依赖 pojo、config、client 的类,修改为 feign-api 模块内的类;

  • 由于 order-service 模块的 application 启动类无法扫描到 feign-api 模块下的 Client 的 Bean 对象,因此会出现如下报错:

  • 需要在 @EnableFeignClients 中加入 clients 属性;

6、Feign 使用问题汇总

(1)Did you forget to include spring-cloud-starter-loadbalancer?

参考了很多解决方法,主要是要区别版本问题:

包含 RELEASE 关键字的版本中,只有下面这样操作才能使用:

  • 首先排除 ribbon 的起步依赖,排除位置在消费者依赖的服务提供者的 dependency;
  • 添加 loadBalancer 依赖;

而在 2021 及更新版本中(也就是没有 RELEASE 关键字的版本),只需要如下操作:

  • 引入 loadBalancer 依赖;
  • 不需要排除 ribbon 的起步依赖;

二、Gateway 网关简介

因此,网关有如下功能:

  • 身份认证和权限校验;
  • 服务路由、负载均衡;(也就是能知道将请求送到哪个微服务)
  • 限制请求流量;

SpringCloud 提供了 2 个组件实现网关功能:

  • gateway;(新版本)
  • zuul;(早期版本)

Zuul 是基于 Servlet 的实现,属于阻塞式编程。

而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux,属于响应式编程的实现,具备更好的性能。

1、搭建网关服务

(1)创建 gateway 模块

  • 创建一个新的模块专门用于网关:gateway-module,需要 SpringBoot 启动类;
  • 引入 SpringCloudGateway 起步依赖;
  • 引入 Nacos 服务发现依赖;

(2)编写路由配置以及 Nacos 地址

  • gateway 中的 routes 属性之前的配置,目的是为了让 gateway 将服务注册到 nacos;
  • gateway 中的 routes 属性之后的配置,指明了可以使用的 service 路由;
  • id:表示这个路由的名字,与其他属性无关;
  • uri:表示路由目标地址,其中 lb 表示负载均衡,后面跟着服务的 name;
  • predicates:指定路由规则,符合规则就可以放行;

(3)测试网关功能

  • 启动 gateway 的 application;
  • 发起请求:localhost:10010/order/queryOrderById/1;
  • 这个请求中,order-service 模块会调用 user-service 的服务;

  • 访问失败,说明我们的网关起作用了,因为我们没有为网关配置 order-service 的路由;

  • 添加对 order-service 的服务的路由后,就可以从 gateway 访问 order 的服务了;

2、路由断言工厂

路由断言工厂 Route Predicate Factory,作用是:

  • 我们在配置文件中写的 predicates 断言规则只是字符串,这些字符串会被 Predicate Factory 读取并解析为路由判断的条件。
  • 每种规则都有各自的断言工厂去解析,比如 Path 有 PathRoutePredicateFactory 断言工厂;

下面以 After 举一个例子:

(1)添加断言规则 After

  • After 断言规则,规定在这个时间之后,才能进行访问; 

(2)访问 order 的服务

  • 发送请求 /order/queryOrderById/1;
  • 理向情况应该是:无法访问;

  • 修改断言规则的时间为:2020-12-31,重启 gateway 模块;

  • 再次发起请求,就可以访问了;

3、路由的过滤器配置

在这个过程中,过滤器就可对请求和响应做出各种各样的处理,这样往下游传递的数据中,就包含了修改后的信息。

具体有什么样的操作,就要看使用哪个过滤器工厂:

下面我们以添加一个请求头为例子:

(1)修改 application 配置文件

  • 给 routes 中的一个路由添加 filters 属性;
  • filters 中添加:- AddRequestHeader=[key],[value];

(2)编写 Controller,获取请求头信息

  • 既然在 filter 中设置了 header:orderStatus=001;
  • 那么请求传递到 Controller 后,一定可以获取到这个头信息;

  • 访问 /order/queryOrderById/1,查看控制台输出;

(3)编写默认过滤器 default-filter

  • 只需要在 routes 同级下,添加 default-filter 属性;
  • 给 default-filter 属性添加:- AddRequestHeader=[key],[value] 等属性值;

  • 访问 /order/queryOrderById/1,查看控制台输出;

4、全局过滤器

全局过滤器是:GlobalFilter。

  • 全局过滤器的作用:处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样,只是作用范围不同。
  • 其中,GatewayFilter 通过配置定义,运行逻辑是固定的。(官方定义)
  • 而 GlobalFilter 的运行逻辑需要自己写代码实现。(自定义)

(1)定义实现类

  • 创建 AuthorFilter 实现类,实现 GlobalFilter 接口;
  • exchange:请求上下文,里面可以获取Request. Response等信息;
  • chain:用来把请求委托给下一个过滤器;
  •  Nono<Void>:返回表示当前过滤器业务结束

(2)定义 filter 方法的运行逻辑

  • 添加 @Component;
  • 添加 @Order,方便多过滤器时指定顺序;
  • 网关中采用的都是基于 WebFlux(响应式编程)的 API,因此使用上与 ServletAPI 不同;
@Order(-1)
@Component
public class AuthorFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1.获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        // 2.获取请求参数中的 author 参数值
        String author = params.getFirst("author");
        // 3.判断是否等于 admin
        if (author.equalsIgnoreCase("admin")) {
            // 放行
            return chain.filter(exchange);
        }
        // 4.不相等,获取响应
        ServerHttpResponse response = exchange.getResponse();
        // 4.1.设置状态码,401表示未登录
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        // 4.2.拦截请求
        return response.setComplete();
    }
}

(3)启动 gateway 的 application

  • 访问 /order/queryOrderById/1/author=admin111,先使用错误的 authon 参数值;
  • 发现返回了 401;

  • 访问 /order/queryOrderById/1/author=admin,使用正确的 authon 参数值;
  • 可以正常访问

5、过滤器链执行顺序

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

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

6、网关的 cors 跨域配置

由于一些页面可能需要从微服务中获取某些数据,此时需要发起 AJAX 请求,那么这就可能属于跨域请求的范畴。(一般我们的 web 页面端口都不会与微服务的相同,所以请求基本上都是跨域请求)

跨域请求不需要在微服务中处理,只需要在网关中处理即可。

跨域问题的 2 个要素:

  • 浏览器、服务端:浏览器禁止请求向服务端发起跨域 ajax 请求,请求会被拦截;
  • 解决方案:CORS;

(1)编写 gateway 的配置文件

  • 各个属性的作用都写在注释中了;

(2)端口 8090 的 AJAX 请求

  • 这里使用 JQuery 发起 AJAX 请求,传递参数为 author=admin;
  • 这里参数是必须传递的,因为发起的请求目标地址是要通过网关的,需要鉴权;
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="JQuery-3.7.0.js"></script>
    <script type="text/javascript">
        $(function() {
            $("#ajaxButton").click(function() {
                $.ajax({
                    url:"http://localhost:10010/order/queryOrderById/1",
                    data: {
                        author : "admin"
                    },
                    type: "get",
                    dataType: "json",
                    success: function(data) {
                        console.log(data);
                    }
                });
            });
        });
    </script>
</head>
<body>
    <a href="http://localhost:10010/order/queryOrderById/1?author=admin"> 发起普通GET请求 </a>
    <input id="ajaxButton" type="button" value="发起 ajax 请求"/>
</body>
  • 发起请求,可以在控制台看到返回的数据;

  • 而如果我们用 <a> 来请求,是会跳转到 10010 得页面的,这就不算跨域请求了;

7、Gateway 网关使用问题汇总

(1)搭建网关服务时,报错 503

Loadbalancer 依赖:

举报

相关推荐

0 条评论