Blage's Coding Blage's Coding
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
Home
算法
  • 手写Spring
  • SSM
  • SpringBoot
  • JavaWeb
  • JAVA基础
  • 容器
  • Netty

    • IO模型
    • Netty初级
    • Netty原理
  • JVM
  • JUC
  • Redis基础
  • 源码分析
  • 实战应用
  • 单机缓存
  • MySQL

    • 基础部分
    • 实战与处理方案
    • 面试
  • ORM框架

    • Mybatis
    • Mybatis_Plus
  • SpringCloudAlibaba
  • MQ消息队列
  • Nginx
  • Elasticsearch
  • Gateway
  • Xxl-job
  • Feign
  • Eureka
  • 面试
  • 工具
  • 项目
  • 关于
🌏本站
🧸GitHub (opens new window)
  • 面试

    • 个人向面试问题
    • 分散的面试问题
    • 面试问题合集
    • 简历的面试问题

      • 散列与哈希算法
      • Redis
      • Netty
      • 网关系统
        • 问题一
        • 问题二
          • 1、网关数据库表的QPS问题?性能瓶颈在哪儿?
          • 2、Netty如何实现断线重连?
          • 3、网关的高可用可以做哪些处理?
          • 4、注册中心负责什么部分?
          • 5、该网关与SpringCloud网关有什么区别?
          • 6、系统想要使用该网关,需要做哪些步骤?
          • 7、网关为什么需要自研?和市面上的区别在哪儿?
        • 8、同一套网关系统怎么映射到不同的后端Dubbo服务上面?
        • 9、部署多套网关,如何进行区分?
        • 10、RPC服务上报过后,协议变了,网关应该如何处理?
        • 11、服务降级方案怎么设计
      • 机试笔试
  • 工具

  • 项目

  • 关于

  • 更多
  • 面试
  • 简历的面试问题
phan
2023-02-01
目录

网关系统

# 网关系统

项目核心模块分为三块内容:

  • Center注册中心
  • Assist-Engine-Core网关算力
  • Provider-SDK服务提供方

# 网关算力Core

整个网关流程(启动服务器、监听连接、请求处理)如下:

  1. 创建线程任务:在调用的线程任务当中,启动Netty服务器
  2. 服务器请求Handler:根据 uri 从 map 获取对应 httpstatement 映射规则,并存入通道会话
  3. 鉴权Handler:请求头拿到 uid 和 token 进行鉴权
  4. 方法调用Handler:解析请求参数,根据 uri 拿到泛化调用,执行、封装、返回调用结果
    • ①获取数据源,建立连接。默认实现以下两种数据源:
      • HTTP:由网关向另一个url发送HTTP请求,调用接口
      • Dubbo:根据方法名获取对应的泛化调用实例,执行RPC方法
    • ②连接对象根据配置信息,包括应用配置+配置中心配置+服务引用配置,获取泛化调用实例
    • ③执行器根据映射规则,封装被调用方法名+参数类型+参数,并通过泛化实例执行。

关键配置对象:

  • Configuration

    • Netty服务端参数,包括IP端口、boss线程、worker线程。

    • 三个Dubbo重实例对象,并配置有缓存Map,key为对应应用ID和接口ID。

    • 网关映射HttpStatement缓存集合,key设置为uri。

    • 泛化调用IGenericReference实例对象缓存,key设置为uri。

  • HttpStatement

    • 应用ID和接口ID
    • 方法名、入参类型、鉴权、uri

# Center注册中心

数据库表设计:

  • 方法信息表;应用ID+接口ID+方法信息(uri,请求方式,参数类型)
  • 接口信息表:应用ID+接口名称+接口版本
  • 应用信息表:应用ID+注册中心
  • 网关服务组表:网关服务ID+地址,对应一个Core实例
  • 网关分配表:网关服务ID <= 绑定 = > 应用ID,一个applicationID下所有接口和方法都交给一个Core核心进行处理

docker挂载和刷新Nginx服务:

  1. 在Center程序运行的容器内部,重写一个conf配置文件,该文件地址挂载在容器外部(阿里云服务器)Nginx配置文件的地址。因此只要容器内部的文件发生变动,那么外部Nginx配置文件也会同步更新。
  2. Nginx刷新reload使配置生效。通过docker-java包,可以在当前容器调用其它容器的服务。具体来说,可以通过容器名拿到该容器的ID,从而exec进入容器内部,执行reload -s指令。

注册节点流程:

  1. 插表:向网关算力表插入记录
  2. 查表:查出所有运行的算力节点,组成代理上游服务器
  3. 更新刷新Nginx配置

Redis事件通知机制:

  • 通道名称为网关地址ID,发送的消息内容为应用名称ID。
  • 每次应用-接口-方法注册成功后,通过redis向通道发送事件。

# Assist

ContextClosedEvent上下文关闭扩展点:

  1. 容器关闭时,需要将core的Netty服务也一并关闭

ApplicationContextAware上下文扩展点:

  1. 首先根据yml配置的网关信息,向“注册中心”注册网关节点。
  2. 查出该网关ID下的所有的接口和方法,加入configuration中的缓存:
    • Dubbo三大变量:applicaiton+registry+reference
    • 方法映射HttpStatement,key为对应请求的uri

通过spring.factories文件,指定GatewayAutoConfig作为外部Bean导入容器管理,通过注解@Configuration与@Bean创建更多Bean对象,其中对于方法@Bean:

  • 方法形参会从Bean容器中找
  • Bean注解的方法返回的对象会作为Bean纳入容器管理

通过Bean方法自动创建的Bean对象如下:

  • Configuration:全局配置信息对象
  • Channel:初始化Netty服务器通道,监听网络请求
  • GatewayApplication:spring拓展点的对象,用于初始化configuration对象

# SDK

核心流程如下:

  1. 通过BeanPost增强方法,获取每个带有自定义注解的接口和方法,并插入数据库。
  2. 向Center发送Redis事件

# Q&A

# 问题一

Netty启动服务端绑定监听的IP地址时,试了两个IP地址都启动失败:

  • 服务器公网IP:直接在服务器上运行正常,但是通过docker运行失败
  • 127.0.0.1:可以正常启动,但是外部无法通过公网IP进行网关通信

经排查了解到,Netty的bind只能绑定本机能感知到的网卡IP。通过docker exec进入容器内部进行ifconfig,发现确实无法感知到外部的公网IP,只能查到虚拟网卡以及127.0.0.1这两个地址。

而对于127.0.0.1外网不能访问,是因为本地环回地址只能在本地访问,只能在容器内通过127.0.0.1进行访问,而在外部其它机器上通过该地址请求不能打到该机器上。

最终解决方案是绑定地址是0.0.0.0,相当于监听该机器上的所有网卡,无论是docker的虚拟网卡还是机器上的网卡,外部发送的数据包都可以监听到。

# 问题二

如何解决网关启动后,后启动的RPC服务加入到Assist网关的缓存?Redis的事件发布通知机制如何进行设计?

  1. Assist首先向center注册中心拉取Redis配置,创建Jedis客户端监听通道消息;创建监听器适配器,指定消息处理函数;将Topic通道与消息处理函数进行绑定。
  2. Center中心提供发送消息的接口

整体流程如下:

  1. Provider:启动时,通过spring拓展点BeanPostProcessor,获取每个注解的Bean对象方法名,将当前服务的“应用、接口、方法”向注册中心注册,最后调用注册中心的Redis事件发送。
  2. Assist:Jedis客户端接收到事件后,获取应用ID,重新拉取该应用ID的所有接口和方法加入缓存。

所有向Center注册中心发送的接口请求,都是通过Hutool的HttpUtil工具发送请求。

# 面试问题

# 1、网关数据库表的QPS问题?性能瓶颈在哪儿?

网关的数据库表主要功能是进行服务注册,以及接口分配,因此一般来说并发量并不会很大,除非真的同时有很多个接口进行注册,那么可以采用分库分表,或者进行分布式部署。

性能瓶颈:网关包含多条链路,比如服务拉取,服务注册,用户请求。其中性能瓶颈主要在这一条链路:用户请求->协议转换->接口获取->泛化调用。包括几个方面:

  • 网络IO:大量请求堆积,超时会导致对应的网络性能问题。可以通过修改Netty网络模型,零拷贝技术提高系统IO性能。
  • Dubbo服务性能:Dubbo服务性能也会影响到API网关整体的性能。提高每个RPC接口的服务性能方法,比如Dubbo的异步调用、负载均衡等
  • 线程池:网关需要处理大量的请求,因此如何设置好线程池参数是关键。也就是Netty的EventLoop线程对象,主要分为两大类,分别是boss和worker线程,每个线程都会维护多个任务队列。
    • boss线程主要处理连接accept事件,一般bind几个IP端口则设置几个boss线程。boss线程会将连接的客户端注册到worker的注册队列中。
    • worker线程主要负责处理用户请求。每个worker线程会不断select查看是否有客户端读写事件,如果没有则不会运行。因此worker线程并不是一直工作的,为了提高CPU的使用效率,一般设置为CPU核心数*2
    • 每个客户端的所有事件都会交由同一个worker进行处理,worker会串行执行所有handler,从而避免线程上下文切换。
  • 内存使用:Netty线程池需要维护多个客户端的队列,因此使用不当可能出现内存溢出、内存泄漏等问题。可以采用减少对象创建等方法进行优化,同时对于网关而言,朝生夕灭的对象比较多,因此JVM的初始新生代需要设置大一些,让大部分对象都直接在新生代存储和消亡,不需要进入老年代。
  • Handler线程池分离:线程池默认使用NIO的线程池。如果自定义的Handler业务处理开销过大,那么可以自定义一个线程池,为Handler指定特定的线程池进行处理,防止业务任务与IO任务同时抢占资源。

# 2、Netty如何实现断线重连?

一般是在worker添加一个定时任务,重新执行客户端连接的逻辑即可。

该网关中,客户端并不需要处理数据,因此无需编写客户端代码,客户端仅通过网络连接与服务端进行通信。

# 3、网关的高可用可以做哪些处理?

方案如下:

  • 异地多机房拓展部署多个网关算力实例,本质上只是负责协议转发,失败了需要配置超时重试。
  • 负载均衡:LVS配置前端负载均衡+ F5硬件负载均衡+ Nginx

# 4、注册中心负责什么部分?

注册中心主要负责服务注册、服务拉取、算力分配等功能,提供给网关操作数据库的功能。而其它网关指的是用于RPC服务的注册中心。

# 5、该网关与SpringCloud网关有什么区别?

网关这个名词实际上叫的比较大。个人理解的话,SpringCloud主要是侧重于不同微服务之间的HTTP通信问题,包括超时、限流、负载。

而API网关主要是侧重于不同RPC的调用管理,以及协议转换。

# 6、系统想要使用该网关,需要做哪些步骤?

如果需要对RPC接口到HTTP协议进行转换,统一管理,可以使用这套网关。本质上是通过编程式控制,向不同RPC接口以及HTTP接口发送请求。看是哪套RPC接口,如果是Dubbo正好本系统进行了开发,将外部的HTTP请求通过网关,打到Dubbo接口,将调用结果通过HTTP协议返回。

  • Dubbo协议:将RPC接口导入SDK,使用相应的注解对接口和方法进行标注,然后启动程序暴露接口,并进行服务注册。另外需要统一外部的HTTP请求路径与对应的RPC接口。
  • HTTP协议:实现一个http到http的协议转换,Spring服务端接口不需要修改。只需注意对应的映射规则。

# 7、网关为什么需要自研?和市面上的区别在哪儿?

市面上有一些网关,个别大厂也有在自研,经过调研主要是一下几个核心问题:

  • 兼容性:市面上开源的网关,系统升级时比较麻烦,可能会出现网关服务依赖包的版本不兼容问题,使用自研的可以减少维护成本。此外,使用的依赖包全程可控,可以减少安全问题。
  • 拓展性:自研最大的好处,在于可以自定义扩展功能,接入处理不同内部自研的协议,同时在调用过程拓展对应的功能。

# 8、同一套网关系统怎么映射到不同的后端Dubbo服务上面?

每个HTTP请求路径,都映射一个Dubbo服务接口方法。

每个网关实例启动时,会向数据库拉取对应的映射关系,并加入缓存。

# 9、部署多套网关,如何进行区分?

网关系统配置了网关<==>Dubbo服务的映射,每个网关ID与服务的映射关系会存入数据库。一个服务下的所有接口对应由同一个网关进行协议转换和转发。

网关启动时,会拉取网关与接口的映射关系,存入映射表缓存当中。

# 10、RPC服务上报过后,协议变了,网关应该如何处理?

协议改变后,需要改动如下:

  • 修改注册中心上报信息,包括RPC服务协议类型
  • 网关需要拓展从HTTP协议到该协议的转换方法,保证该RPC服务能够正确被网关转发和调用

# 11、服务降级方案怎么设计

服务降级主要包括:限流、熔断、降级,可以通过插件实现。在RPC服务启动注册的时候,同时启动服务治理配置,配置相应的操作,比如降级之后直接返回错误码。

编辑 (opens new window)
#面试
上次更新: 2024/04/08, 16:52:36
Netty
机试笔试

← Netty 机试笔试→

Theme by Vdoing | Copyright © 2023-2024 blageCoder
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式