opencv案例实战——银行卡模式匹配识别

news2024/9/28 1:20:21

系列文章目录

1.图像读取及其通道与灰度
2.图像填充与图像融合
3.图像滤波
4.图像阈值
5.腐蚀与膨胀
6.图像梯度
7.边缘检测
8.轮廓与轮廓特征


银行卡模式匹配识别

  • 系列文章目录
  • 前言
  • 案例介绍
  • 划分模板
    • 1.思路
    • 2.获取边缘
    • 3.获取外接矩形
  • 图像预处理
    • 切割
    • 礼帽操作
  • 分割数字块
    • sobel算子
    • 膨胀和腐蚀
  • 外接矩形
    • 画出轮廓区域
    • 画外接矩形
  • 模板匹配
    • 分割数字块
    • 读入模板
    • 匹配数字
    • 代码下载


前言

在之前的几篇文章中我们已经介绍了opencv的一些基础知识,本篇文章我们将结合一个案例运用之前的知识并且学习opencv中模式匹配的应用。
演示视频如下:

基于opencv图像处理的卡号检测效果演示(附源码)

用到的知识点如果有不清楚的可以查看上面列出来的系列文章。具体代码会放在最后。

案例介绍

我们有若干个如下图的银行卡图片
在这里插入图片描述
我们的目的是通过一些图像处理操作检测出银行卡中的卡号,除了银行卡图片外我们还有一个模板图片
在这里插入图片描述
模板图片是10个数字
为了方便展示图像,我们依然先定义一个用于展示图像的函数

def cv_show(img, name):
    cv2.imshow(name, img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

同时为了统一处理,我们还需要有一个调整图像大小的函数

def img_resize(img, hight):
    (h, w) = img.shape[0], img.shape[1]
    r = h / hight
    width = w / r
    img = cv2.resize(img, (int(width), int(hight)))    
    return img

划分模板

1.思路

  1. 先读出模板图像
  2. 进行灰度化和二值化
  3. 边缘检测
  4. 求外接矩形
  5. 根据外接矩形裁剪

2.获取边缘

img = cv2.imread("./template/ocr_a_reference.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(cy, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

3.获取外接矩形

显示裁剪结果

for i in range(len(contours)):
    x, y, w, h = cv2.boundingRect(contours[i])
    plt.subplot(3, 4, i+1)
    plt.imshow(thresh[y:y+h, x:x+w],cmap = plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    
plt.show()

在这里插入图片描述
裁剪并保存

import os

for i in range(len(contours)):
    x, y, w, h = cv2.boundingRect(contours[i])
    cv2.imwrite(os.path.join('cuted_template', str(9-i)+'.jpg'), thresh[y:y+h, x:x+w])

保存好的结果如下
在这里插入图片描述
这些将用作后边的模板匹配的模板

图像预处理

我们还需要对银行卡进行图像的预处理

切割

我们留意到,银行卡只有一部分区域是我们需要的卡号,所以先进行一下切割

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name))
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3 * 2]
    plt.subplot(3, 2, i)
    plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB), cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述

礼帽操作

我们发现不同卡上的数字明暗程度不一样,我们可以通过礼帽操作突出更亮的区域,即突出数字

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name), cv2.IMREAD_GRAYSCALE)
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 10))
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, rectKernel) 

    plt.subplot(3, 2, i)
    plt.imshow(tophat, cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述

分割数字块

通过观察发现,经过上述处理过后,依然会有一些干扰,如果直接做轮廓很难区分
设想一下,我们如果在有干扰的情况下通过轮廓画出矩形框会怎样?当然是不属于数字的部分也会被画上矩形框,那如何将他们区分开呢?我们很自然可以想到用面积或者周长,但是因为各个数字是分离的,数字的面积或周长可不一定比干扰大,所以我们要把数字分成4组,每组称为一个数字块,这样一来数字部分的就比干扰要大得多了

sobel算子

在前面的文章中我们了解到,sobel算子当使用Sx求得Gx时,会更加注重左右方向的轮廓,所以进行sobelx操作后我们图像中的数字会变得比原先略宽,虽然现在已经看不出是数字了,不过没关系,我们要的只是数字块而不是数字本身。

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name), cv2.IMREAD_GRAYSCALE)
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 5))
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, rectKernel) 
    sobelx = cv2.Sobel(tophat, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    minval, maxval = np.min(sobelx), np.max(sobelx)
    sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
    sobelx = sobelx.astype('uint8')
    plt.subplot(3, 2, i)
    plt.imshow(sobelx, cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述

膨胀和腐蚀

那么如何让这些变宽的"数字"粘连在一起呢,那必然是膨胀和腐蚀啊

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name), cv2.IMREAD_GRAYSCALE)
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, rectKernel) 
    sobelx = cv2.Sobel(tophat, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    minval, maxval = np.min(sobelx), np.max(sobelx)
    sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
    sobelx = sobelx.astype('uint8')
    dilate = cv2.dilate(sobelx, rectKernel, 10)
    erosion = cv2.erode(dilate, rectKernel, 10)
    plt.subplot(3, 2, i)
    plt.imshow(erosion, cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述
可以看到数字块的雏形已经有了,但是还不够简洁,我们进行二值化之后在进行一次膨胀和腐蚀

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name), cv2.IMREAD_GRAYSCALE)
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, rectKernel) 
    sobelx = cv2.Sobel(tophat, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    minval, maxval = np.min(sobelx), np.max(sobelx)
    sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
    sobelx = sobelx.astype('uint8')
    dilate = cv2.dilate(sobelx, rectKernel, 10)
    erosion = cv2.erode(dilate, rectKernel, 10)
    
    ret, thresh = cv2.threshold(erosion, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
    dilate = cv2.dilate(thresh, sqKernel, 10)
    erosion = cv2.erode(dilate, sqKernel, 10)
    plt.subplot(3, 2, i)
    plt.imshow(erosion, cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述
可以看到效果比前面略好,但是进步不大,所以我们的膨胀和腐蚀就先到此为止了

外接矩形

画出轮廓区域

i = 1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name))
    img = img_resize(img,200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
    sobelx = cv2.Sobel(tophat, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    minval, maxval = np.min(sobelx), np.max(sobelx)
    sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
    sobelx = sobelx.astype('uint8')
    dilate = cv2.dilate(sobelx, rectKernel, 10)
    erosion = cv2.erode(dilate, rectKernel, 10)
    
    ret, thresh = cv2.threshold(erosion, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
    dilate = cv2.dilate(thresh, sqKernel, 10)
    erosion = cv2.erode(dilate, sqKernel, 10)
    
    contour, hierarchy = cv2.findContours(erosion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    res = cv2.drawContours(img.copy(), contour, -1, (0, 0, 255), 3)
    plt.subplot(3, 2, i)
    plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB), cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述

画外接矩形

我们为了后续的尝试更加方便,将外接矩形框起来的数字块保存下来

i = 1
k=1
plt.figure(figsize=(50, 10))
for name in os.listdir('./images'):
    img = cv2.imread(os.path.join('./images',name))
    img = img_resize(img, 200)
    h = img.shape[0]
    img = img[h//2:h//3*2]
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    
    rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
    sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
    sobelx = cv2.Sobel(tophat, cv2.CV_64F, 1, 0, ksize=3)
    sobelx = cv2.convertScaleAbs(sobelx)
    minval, maxval = np.min(sobelx), np.max(sobelx)
    sobelx = (255 * ((sobelx - minval) / (maxval - minval)))
    sobelx = sobelx.astype('uint8')
    dilate = cv2.dilate(sobelx, rectKernel, 10)
    erosion = cv2.erode(dilate, rectKernel, 10)
    
    ret, thresh = cv2.threshold(erosion, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
    dilate = cv2.dilate(thresh, sqKernel, 10)
    erosion = cv2.erode(dilate, sqKernel, 10)
    
    contour, hierarchy = cv2.findContours(erosion, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    
    res = img.copy()
    
    for cnt in  contour:
        x, y, w, h = cv2.boundingRect(cnt)
        if w * h > 300:
            res = cv2.rectangle(res, (x, y), (x+w, y+h), (0, 0, 255), 1)
            cv2.imwrite(os.path.join('./cuted_images','{}.jpg'.format(k)), res[y:y+h, x:x+w])
            k+=1
        
    plt.subplot(3, 2, i)
    plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB), cmap=plt.cm.gray)
    plt.xticks([])
    plt.yticks([])
    i+=1
        
plt.show()

在这里插入图片描述
保存结果为
在这里插入图片描述

模板匹配

分割数字块

在前面我们得到了数字块,但是每个数字块是四个数字,所以我们需要像对模板进行分割一样分割数字块

img = cv2.imread('./cuted_images/1.jpg')
img = img_resize(img, 200)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
res = img.copy()
for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    res = cv2.rectangle(res, (x, y), (x+w, y+h), (255, 0, 0), 5)
    
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()

在这里插入图片描述

读入模板

我们这里使用字典的形式保存模板

digits = {}
for i in range(10):
    digits[i] = cv2.resize(cv2.imread('./cuted_template/{}.jpg'.format(i)), (100, 150))

for i in range(10):
    plt.subplot(3, 4, i+1)
    plt.imshow(digits[i])
    plt.title(str(i))

plt.show()

在这里插入图片描述

匹配数字

在进行匹配之前,我们先要介绍两个函数:cv2.matchTemplate和cv2.minMaxLoc

cv2.matchTemplate
这个函数可以用来进行模板匹配,第一个参数是待匹配的图像,第二个参数是匹配模板,第三个参数是匹配方式
第三个参数可以选值如下

参数值评价方式含义
cv.TM_SQDIFF判断 minVal 越小,效果越好计算模板与目标图像的方差,由于是像素值差值的平方的和,所以值越小匹配程度越高
cv.TM_SQDIFF_NORMED判断 minVal 越接近0,效果越好范化的cv.TM_SQDIFF,取值为0-1之间,完美匹配返回值为0
cv.TM_CCORR判断 maxVal 越大,效果越好使用dot product计算匹配度,越高匹配度就好
cv.TM_CCORR_NORMED判断 maxVal 越接近1,效果越好范化的cv.TM_CCORR,0-1之间
cv.TM_CCOEFF判断 maxVal 越大,效果越好采用模板与目标图像像素与各自图像的平均值计算dot product,正值越大匹配度越高,负值越大图像的区别越大,但如果图像没有明显的特征(即图像中的像素值与平均值接近)则返回值越接近0;
cv.TM_CCOEFF_NORMED判断 maxVal 越接近1,效果越好范化的cv::TM_CCOEFF,-1 ~ 1之间

各种算法的特点

算法特点
TM_CCORR擅长区分出(有颜色差异的)不同区域
TM_SQDIFF运算过程简单,匹配精度高,运算量偏大,对噪声非常敏感
TM_CCOEFF算法计算量小,简单易实现,很适合于实时跟踪场合,但跟踪小目标和快速移动目标时常常失败
img = cv2.imread('./cuted_images/1.jpg')
img = img_resize(img, 200)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

number = ""

for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    cur_img = res[y:y+h, x:x+w].copy()
    cur_img = cv2.resize(cur_img, (100, 150))
    scores = []
    for i in range(10):
        result = cv2.matchTemplate(cur_img, digits[i], cv2.TM_CCOEFF)
        (_, score, _, _) = cv2.minMaxLoc(result)
       
        scores.append(score)   
    number = str(np.argmin(scores))+number
    
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.title(number)
plt.show()

在这里插入图片描述

代码下载

完整的模块化代码已放到GitHub
在这里插入图片描述
在这里插入图片描述

GitHub下载地址:https://github.com/AiXing-w/template-match-banck-card

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode题解 二叉树(十):654 最大二叉树;617合并二叉树

654 最大二叉树 medium 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下: 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给定的数组构…

RHCEansible 编写playbook---yaml

YAML 语言特性 YAML的可读性好 YAML和脚本语言的交互性好 YAML使用实现语言的数据类型 YAML有一个一致的信息模型 YAML易于实现 YAML可以基于流来处理 YAML表达能力强,扩展性好YAML的三种数据结构 对象: 键值对的集合,又称为映射、哈希、字典…

Map数据结构详解

Map Object本质上是键值对的集合(Hash结构),但Object只能将字符串当做键,这就给Object带来了很大的限制。 let data {} let s {name : 东方不败} data[s] 西方求败// 如果键传入非字符串的值,会自动为字符串 cons…

思科路由器DHCPv6服务配置

配置如下 Router>ena Router#conf t Router(config)#host R1 R1(config)#ipv6 unicast-routing R1(config)#int g0/0 R1(config-if)#ipv6 add 2001:1::1/64 R1(config-if)#no sh R1(config-if)#exit R1(config)#service dhcp R1(config)#ipv6 local pool v6pool …

CTF中的PHP特性函数(中)

前言 上篇文章给大家带来了PHP中最基本的特性,不知道大家学习的怎样了,回顾上文,我们讲了MD5强弱碰撞以及正则匹配的绕过,总体来看还是很简单的,下面给大家带来新的PHP特性讲解,会稍微比上一篇难一些。 i…

开源工作流引擎如何支撑企业级 Serverless 架构?

作者:董天欣(雾雾) Serverless 应用引擎(SAE)是一款底层基于 Kubernetes,实现了 Serverless 架构与微服务架构结合的云产品。作为一款不断迭代的云产品,在快速发展的过程中也遇到了许多挑战。如…

2022年统一大市场研究报告

第一章 行业概况 国内统一大市场指的是在全国范围内,在充分竞争以及由此形成的社会分工基础上,各地区市场间、各专业市场间形成了相互依存、相互补充、相互开放、相互协调的有机的市场体系。在这种市场体系下,商品和要素能够按照价格体系的调…

干货 | 在Docker 上搭建持续集成平台 Jenkins

Docker是一个开源的应用容器引擎,基于 Go 语言开发,Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的系统。Docker 是世界领先的软件容器平台,Docker 官方的口号是”调试你的应用&…

Three.js学习(二)three.js的一些基本操作

文章目录1.鼠标操作三维场景旋转、移动和缩放2.场景中添加新的三维图形3.设置材质效果4.光源效果1.鼠标操作三维场景旋转、移动和缩放 使用THREE的OrbitControls控件,可以实现鼠标控制三维图形的操作。主要是通过监听鼠标操作,控制相机的三维参数。 imp…

python数据分析-matplotlib、numpy、pandas

数据分析概述数据分析就是用适当的方法对收集来的大量数据进行分析,帮助人们在一处判断,以便采取适当行动数据分析流程jupyter notebook说明matplotlib:Matplotlib — Visualization with Python基本要点能将数据进行可视化,更直观…

docker基本命令演示

docker拉取redis镜像演示首先前往:https://hub.docker.com找到需要的redis镜像然后 点击之后使用命令 docker pull redis 拉取redis 成功之后使用命令 docker images 查看本地的镜像使用命令导出镜像到指定文件 docker save -o redis.tar redis:latestdocker save…

Android13适配

遇到的一些问题 1.WebChromeClient的 API onReachedMaxAppCacheSize 没了 onReachedMaxAppCacheSize overrides nothing // 扩充缓存的容量override fun onReachedMaxAppCacheSize(spaceNeeded: Long, totalUsedQuota: Long, quotaUpdater: QuotaUpdater) {quotaUpdater.upda…

QT学习笔记(下)

项目实践 前言 本项目的实践主要是以翻金币的项目为例,进行QT项目的实践。游戏分为3个场景,分别是mainscene主场景、chooselevelscene选择关卡场景、playscene游戏场景,以上的三个场景是按顺序实现的。并且定义了两个自定义的QPushButton按…

数据库,计算机网络、操作系统刷题笔记24

数据库,计算机网络、操作系统刷题笔记24 2022找工作是学历、能力和运气的超强结合体,遇到寒冬,大厂不招人,可能很多算法学生都得去找开发,测开 测开的话,你就得学数据库,sql,oracle…

c++下 ADO+配置数据源连接oracle数据库

测试环境:在本地局域网内远程连接服务器端的oracle数据库,VS2013、ADO方式。2、本地安装oracle数据库客户端,具体是安装32位还是64位的数据库客户端,取决于我们编译的程序是32位的还是64位的(和计算机的系统位数没有关…

首款通过! 机器学习服务活体检测算法荣获CFCA权威安全认证

随着人脸识别技术在金融、医疗等多个领域的加速落地,网络安全、信息泄露等问题愈为突出,用户对应用稳定性和安全性的要求也更为严格。为保障各行业高效稳定的开展业务,提前发现和应对潜在安全风险,华为 HMS Core 机器学习服务&…

【回答问题】ChatGPT上线了!给我推荐20个比较流行的图神经网络模型

给我推荐20个比较流行的图神经网络模型 很高兴为您推荐流行的图神经网络模型。这里是我的推荐列表: 卷积神经网络 (CNN) 递归神经网络 (RNN) 自注意力机制 (Transformer) 双向递归神经网络 (BRNN) 门控循环单元 (GRU) 长短期记忆网络 (LSTM) 双流图神经网络 (Bi-D…

鲁大师2022牛角尖颁奖盛典落幕,年度最强产品揭晓!

1月4日,鲁大师2022年度牛角尖颁奖晚会在湖北襄阳成功举办。鲁大师的”牛角尖”奖是由过去一年上亿用户通过鲁大师测试得到的真实数据,以及鲁大师实验室通过专业的测试规范共同缔造的硬件奖项。颁发给的都是各大PC、手机、电动车领域最优秀、最顶尖的产品…

JAVA 的内存泄露分析

背景 前不久,上线了一个新项目,这个项目是一个压测系统,可以简单的看做通过回放词表(http请求数据),不断地向服务发送请求,以达到压测服务的目的。在测试过程中,一切还算顺利&#x…

统一网关Gateway

网关功能: 身份验证、权限校验服务路由、负载均衡请求限流 网关实现技术: gatewayzuul Zuul是基于Servlet的实现,属于阻塞式编程。而SpringCloudGateway则是基于Spring5中提供的WebFlux,属于响应式编程的实现,具备更…