pycharm 突然无法连接远程服务器

具体报错如下:

%title插图%num

Error running ‘train’: While creating remote tunnel for SshjSshConnection(<crc32=zb65bg>@<crc32=1ljowo7>)@18ad558d: localhost:63342 <== localhost:63342

或者:

%title插图%num

Error running ‘train’: Can’t run remote python interpreter: Can’t get remote credentials for deployment server zhangli@192.168.55.152:22

解决办法:

Delete ~/.pycharm_helpers from remote host, and kill all opened SSH sessions that may still run in the background or reboot the remote host and try again.

我的做法:删除远程服务器中用户目录下的~/.pycharm_helpers 文件夹,删除本机pycharm中的远程解释器,清空Deployment中所有代码同步服务。然后,再重启pycharm,重新设置远程解释器,代码同步服务,问题解决。

你可能也会掉进这个简单的 String 的坑

作者的同学是某大公司高级开发工程师,某日收到不少错误告警信息,于是便去开始排查。

跟踪日志发现是某个服务抛出的异常信息,奇怪的是这个服务上线也有一段时间了。之前很少看到类似的错误信息,*近偶尔多了起来。

后来才定位到是因为服务调用了某外部接口,发现对方对参数长度做了限制,如果输入参数超过 1000 bytes,就直接抛异常,代码类似如下:

  1. /**
  2.  * @param status
  3.  * @param result, the size should less than 1000 bytes
  4.  * @throws Exception
  5.  */
  6. public XXResult(boolean status, String result) {
  7.     if (result != null && result.getBytes().length > 1000) {
  8.         throw new RuntimeException(“result size more than 1000 bytes!”);
  9.     }
  10.   ……
  11. }

心想,这还不简单,咱们的 result 也不是什么关键性的东西,你有限制,我直接 trim 一下不就行了?

%title插图%num

%title插图%num解决方案

于是三下五除二,给搞了个 trim 方法,支持传不同参数按需 trim,代码如下:

  1. /**
  2.  * 将给定的字符串 trim 到指定大小
  3.  * @param input
  4.  * @param trimTo 需要 trim 的字节长度
  5.  * @return trim 后的 String
  6.  */
  7. public static String trimAsByte(String input, int trimTo) {
  8.     if (Objects.isNull(input)) {
  9.         return null;
  10.     }
  11.     byte[] bytes = input.getBytes();
  12.     if (bytes.length > trimTo) {
  13.         byte [] subArray = Arrays.copyOfRange(bytes, 0, trimTo);
  14.         return new String(subArray);
  15.     }
  16.     return input;
  17. }

再在需要调用外部服务的地方,先调用这个 trimAsByte 方法,一顿操作连忙上线,一切完美~

%title插图%num

%title插图%num灾难现场

一切完美,作者也是这样认为的。然后幸福总是短暂的。

经过一段时间后(前面也提到,业务场景确实是偶发的),相同的错误仍然发生了。

简直不敢相信,都 trim 了为啥还会超出?你也帮忙想想,是哪里的问题?

%title插图%num

%title插图%num

看看上面的例子(为了方便展示,简单修改文首代码了下),

trimAsByte("WeChat:tangleithu"8)

输入字符串 WeChat:tangleithu 太长了,只 trim 到剩下 8 个字节,对应的字节数组是从 [87,101,67,104,97,116,58,116,97,110,103,108,101,105,116,104,117] 变为了 [87,101,67,104,97,116,58,116],字符串变成了 WeChat:t ,结果正确。

其实在写这个方法的时候还是太草率了,本应该很容易想到中文的情况的,我们来试试:

  1. trimAsByte(“程序猿石头”8)

%title插图%num

看上述截图,悲剧了,输入程序猿石头,3 个字节一个汉字,一共 15 个字节 [-25,-88,-117,-27,-70,-113,-25,-116,-65,-25,-97,-77,-27,-92,-76],trim 到 8 位,剩下前 8 位 [-25,-88,-117,-27,-70,-113,-25,-116] 也正确。再 new String,又变成3 个 “中文” 了,虽然第 3 个“中文”,咱也不认识,咱也不敢问到底读啥,总之再转换成字节数组,长度多了 1 个,变成 9 了。

%title插图%num

问题算是定位到了。

%title插图%num不禁要问,为什么?

来看看这个 String 的构造函数,看看上面注释才发现,其实我们忽略了一个很重要的概念,就是编码方式。

  1. /**
  2.  * Constructs a new {@code String} by decoding the specified array of bytes
  3.  * using the platform’s default charset.  The length of the new {@code
  4.  * String} is a function of the charset, and hence may not be equal to the
  5.  * length of the byte array.
  6.  *
  7.  * <p> The behavior of this constructor when the given bytes are not valid
  8.  * in the default charset is unspecified.  The {@link
  9.  * java.nio.charset.CharsetDecoder} class should be used when more control
  10.  * over the decoding process is required.
  11.  *
  12.  * @param  bytes
  13.  *         The bytes to be decoded into characters
  14.  *
  15.  * @since  JDK1.1
  16.  */
  17. public String(byte bytes[]) {
  18.     //this(bytes, 0, bytes.length);
  19.     checkBounds(bytes, offset, length);
  20.     this.value = StringCoding.decode(bytes, offset, length);
  21. }

当我们用默认的构造函数 new String 的时候,只是用了系统默认的编码(本文是“UTF-8”)去尝试解码,构造出字符串。

所以,当我们在用字节数组(字节流)来表达具体的语义的时候,一定要约定好以什么方式进行编码,本文不具体阐述编码问题了。下面用一个例子来解释上文的现象:

%title插图%num

[-25,-88,-117,-27,-70,-113,-25,-116,-65,-25,-97,-77,-27,-92,-76] 仍然用这串字节数组来实验,这串字节数组,如果用 “UTF-8” 编码去解释,那么其想表达的语义就是中文“程序猿石头”,从上文标注的 1,2,3 中可以看出来,没有写即用了系统中的默认编码“UTF-8”。

假设按照 “GBK” 来解释(标注 4),就是表达的 “绋嬪簭鐚跨煶澶�”,注意看下其中的 � 是不是似曾相识;

注意标注 5,通过 GBK 解释构造字符串后,再通过默认的 “UTF-8” 获取字节数组,长度就变成 24 了,然后还通过 “GBK” 编码得到的字节数组长度为 15(标注 6),再试图构造字符串(标注 7),其中“程序猿石头”的“头”字,已经没了。说明这个转换过程中,其实信息已经被丢了。

上面的 � 其实是 UNICODE 编码方式中的一个特殊的字符,也就是 0xFFFD(65535),其实是一个占位符(REPLACEMENT CHARACTER),用来表达未知的、没办法表达的东东。上文中在进行编码转换过程中,出现了这个玩意,其实也就是没办法准确表达含义,会被替换成这个东西,因此信息也就丢失了。你可以试试前面的例子,比如把前 8 个字节中的*后一两个字节随便改改,都是一样的。

%title插图%num

程序猿石头:65533 示例

%title插图%num

总结

总结一下,其实本来是一个很简单的问题,却经过几次修改才*终解决,说明对 “基础” 掌握得还是不够,一个重要的点是,在处理二进制数据的时候,一定要联想到 “编码” 方式。

另外,提醒我们,看似简单的问题,我们往往容易忽略。比如如果单纯看到文中提到的这个trim 方法,其实很容易写个单元测试就能尽早发现有问题;

一文告诉你雾计算与云计算的区别及对物联网的价值!

雾计算是一种分布式计算结构。将数据和应用程序等资源放置在数据源和云之间的逻辑位置。雾计算的优点之一是让许多用户同时连接到互联网上。从本质上说,它提供的网络和服务与基于云的解决方案提供的相同,但增加了分散式网络的安全性。

%title插图%num云计算和雾计算的区别

云计算

云计算是提供地理分布的计算和存储资源。计算可以在各种平台上进行,包括公共云和私有云。

云计算平台通过可伸缩系统在用户之间共享和均衡工作负载。从本质上讲,云计算具有从非站点位置存储和恢复数据的能力。

云计算是传统手机变得“智能”的主要原因之一。手机没有足够的内置空间来存储和访问应用程序和服务所需的数据。所有的数据都传输到云上,以提供我们需要的服务。尽管如此,云计算技术仍然面临着一个挑战——带宽限制。

雾计算

在不久的将来,雾计算将主宰整个行业。雾的主导将受更接近用户设备的数据源所驱动,设备将在本地而不在云中执行处理。不过,设备也面临物理上的限制——低功耗和小尺寸。

本地处理数据的能力显得更为重要,雾计算增加了数据的安全性。随着物联网的发展,越来越多的设备被添加到网络中。每个设备都通过无线连接进行数据传输和接收。

雾计算能够有效地存储和访问数据。雾计算将分散在网络中的边缘计算节点组网,使其虽然在地理上分布,但节点之间仍能有组织的通信。

使用雾计算涉及到互连边缘设备的复杂过程。边缘设备包括传感器、存储系统和网络基础设施,它们协同工作以捕获和分发数据。

然而,雾计算的灵活性,以及它从集中的云和网络的边缘设备收集和处理数据的能力,使其成为处理我们今天所面临的信息过载的*有用的方法之一。

%title插图%num

图片来源:nikhomk panumas;pexels

%title插图%num雾计算和边缘计算是一回事吗?

雾计算有时也被称为“边缘计算”。边缘计算的目的是通过将数据存储在离“地面”更近的地方来解决问题。换句话说,边缘将数据存储在存储设备和本地计算机中,而不是通过云端运行所有数据。

从本质上说,雾计算支持快速响应时间,减少网络延迟和流量,并节约骨干带宽,以实现更好的服务质量(QoS)。它还支持将相关数据传输到云端。

IDC估计,到2025年底,全球大约有45%的数据将被移向网络边缘。雾计算被认为是未来几年唯一能够抵御人工智能、5G和物联网的技术。

IDC的另一项研究预测,2020年边缘设备将产生全球10%的数据。边缘设备将满足雾计算更有效解决方案的需求,从而减少延迟。

边缘计算

边缘计算基本上是雾计算的一个子集。它是在数据出现的地方附近进行处理。雾计算支持更有效的数据处理,从而减少数据延迟的可能性。

可以将雾计算看作是一种处理数据的广义方法,从数据生成的地方到数据存储的地方。边缘计算指的是在数据产生的地方附近对数据进行处理;而雾计算封装了从边缘到末端传输数据所需的边缘处理和网络连接。

随着物联网设备的能力不断增强,如可编程自动化控制器。自动化控制器执行数据处理、通信和其他任务。利用雾计算,数据从端点传输到网关;然后将数据传输到云中进行处理和回传。地理分布的基础设施与云服务保持一致,以便能够以*小的延迟进行数据分析。

雾和边缘计算都有助于更快地将数据转化为可操作的见解,从而使用户能够更快、更明智地做出决策。雾和边缘允许公司更有效地使用带宽,同时增强安全和解决隐私问题。因为雾节点可以安装在任何有网络连接的地方,因此雾计算在工业物联网应用中越来越受欢迎。

%title插图%num雾计算在物联网中的作用

当一个设备或应用程序产生或收集大量信息时,数据存储就会变得越来越复杂和昂贵。在处理这些数据时,网络带宽也变得昂贵,需要大型数据中心来存储和共享这些信息。

雾计算已经替代了传统处理数据的方法。雾计算收集数据和分配计算、存储网络连接的资源和服务。它显著降低了能源消耗,*小化了空间和时间复杂度,并*大化了数据的效用和性能。

智慧城市

让我们以智慧城市为例。城市中所有物联网设备传输、存储和访问的数据量不断增加,这将需要一种新的基础设施来处理这些数据量。正是这些应用需要雾计算来体现物联网带来的全部价值。

公共事业

智慧城市中的供水、医院、执法、运输和应急管理应用需要*新的数据和技术来提供信息和服务,以支持其高效运作。

关于水泄漏、碳排放、公共设施损坏等信息可以用于政府监管、改进操作、挽救生命和提高效率。

雾计算不会从一个地方移动到另一个地方。相反,雾是一种部署物联网网络的方法,可以提供*好的投资回报。

雾计算的优点

雾计算可用于处理大量数据、网络事务和快速响应的应用程序。雾计算包括实时、混合和自主数据处理中心,这些数据处理中心可以提高运行效率和安全性。此外,雾计算可以确保系统的可用性和*优性,而无需在电力、数据中心安全性和可靠性方面进行投资。

雾计算通过集中多个节点的计算资源来降低开销成本。雾节点的位置是根据它们的可用性、效率和使用情况来选择的。它还减少了企业数据中心的负载。减少数据流量是雾计算的另一个主要优势。

许多公司正在使用雾计算来部署分布在许多地方的软件应用程序。公司在一个网络上部署许多系统,以实现更高的效率和可达性。

从根本上说,雾计算为企业处理数据提供了更大的灵活性。对于某些应用,数据处理必须尽可能快,例如在制造业,连网设备应该尽快对事故做出反应。

雾计算还能使公司实时了解他们的客户或员工的情况。企业有望利用物联网技术抓住新的机遇,增加利润。随着雾计算的实施,这项技术有可能为政府、公司甚至个人用户节省一大笔钱。

%title插图%num结论

随着云技术不断渗透到企业环境中,雾计算的使用也将继续增加。云计算通过弹性计算来分配工作负载,从而实现对云中的数据的实时处理。

边缘计算是物联网雾计算领域的一个重要领域。边缘计算是将计算资源部署在云之外的网络边缘的技术。它允许网络边缘的计算资源被访问、分析。

雾计算解决方案将使公司能够在物联网中实现实时计算。因此,物联网雾计算市场将成为云计算市场的主要贡献者。

企业使用云计算低效益怎么办?区块链或成良药

在云资源浪费现象激增的大环境中,企业如何才能寻得一线生机?

借助云计算服务,企业和开发人员能够通过互联网远程组织资源并运行工作负载。

全球范围内,有数百万人都在使用由云计算实现的无缝数据共享和存储解决方案。自年初以来,新型冠状病毒的大流行迫使世界各地的公司将各自的业务转移为远程线上操作,全球云计算市场持续增长,仅本季度增幅就达33%,共计65亿美元。

实际上,当今我们提到的云计算*早可以追溯到20世纪50年代。当时,一间机房仅可以容纳一组IBM笨重的大型机基础设施。而如今,随着科学技术的快速发展,同样大小的机房可以容纳数百台服务器。

%title插图%num20世纪50年代,被笨重巨大的IBM大型机占据的机房

但是,不断上涨的价格,超大规模巨头的垄断,以及海量用户数据集中化带来的安全风险,仍然是所有供应商和消费者们需要面临的问题。

区块链技术能够为提高供应链透明度及产权转移等多个领域提供可行的解决方案,因此,它自然成为了解决企业云计算问题的不二之选。

接下来,我们将深入探讨一下区块链对现有云计算模型进行改进的几种方式。

%title插图%num

区块链为摆脱市场垄断现状提供了一条出路

随着人们对云计算的需求呈指数级增长,亚马逊网络服务(Amazon Web Services)、微软Azure和谷歌云等云计算巨头的垄断地位也在逐步加强。与移动操作系统和搜索引擎类似,这一领域的风向也是由少数几家供应商掌控并主导的。

市场对于按需交付和存储IT资源的需求日益增长,世界上*大的几家云计算公司迅速抓住并以此占据了巨大的市场份额。

2020年,仅AWS一家公司就占有云计算市场32%的份额,其产生的收入超过其他三大竞争对手的总和。

%title插图%num

Canalys在2020年第三季度末强调了5大企业所占的市场份额

各商业巨头正在逐步扼制其小型竞争对手的发展,然而,区块链技术为小型企业和开发者们提供了另一种解决方案。

借助以区块链为基础的模式,企业级云计算解决方案可以在平等的条件下进行竞争。消费者可以获得成本低廉的存储和计算服务——不需要信任少数中央机构,也不必为此支付额外费用。

%title插图%num

区块链提供了更具成本效益的解决方案

目前,由于行业巨头们掌握着整个产业王国的金钥匙,因此,对于很多企业来说,在日常场景中使用云计算的成本效益并不高。

*的云计算提供商,如谷歌,需要大量的空间和资源来运行如此大规模的服务器群组,不出所料,这都将反应在终端用户的成本上。

如果用户*终真的能物尽其用的话,那这笔支出倒也算花得值得,但很不幸,事实往往并非如此,在云计算上的支出有三分之一以上都被浪费了。

在这种数据处理成本上升的情况下,区块链技术可以帮助我们绕过典型云存储所带来的这部分开销。

以区块链为基础的云服务提供商不需要因运行庞大的处理群组而向终端用户收取更高的费用,与原本中心化云服务提供商的成本相比,其成本要远小得多,但计算能力却不打折扣。

这是促使人们关注Gather等创新公司的几个卖点之一。通过使用区块链技术,去中心化的云服务提供商可以为企业提供廉价且可靠的处理能力,同时保证较高的传输速度和整体安全水平。

Gather创建了一个以区块链为基础的模式,各方参与者都能从中受益——网络用户可以进入网站来分享自己电脑闲置的处理能力,网站所有者从这些用户身上赚取收益,企业则可以从低成本的计算能力中获益。

%title插图%num

区块链解决了重要的安全问题

在降低业务成本的同时,去中心化云计算还消除了目前中心化云计算模式中存在的数据泄露风险。

全球情报公司IDC的一项研究结果显示,近80%的受访企业在过去18个月里曾至少经历过一次云数据泄露事件。

单点数据存储带来了更高的数据泄露风险。区块链技术提供的分布式数据存储解决方案可以将数据文件分解成多个分段,经过加密后存储在多个节点中,通常还会跨越多个不同的国家。这样一来,便可以大大提升网络安全性。

区块链可以对原本被浪费的闲置计算能力加以利用

当今世界,一些科技巨头会依赖于志愿者用户提供的闲置设备网络,利用综合处理能力来完成资源密集型计算任务。

%title插图%num

IBM的World Community Grid依赖于志愿者用户提供的闲置设备的计算能力

一些区块链公司进一步改进了这种模式,提供开创性解决方案,以激励那些贡献闲置计算能力的人。

Ankr依托共享经济的经济模式,向能够腾出多余存储和计算能力的人支付报酬,并将其与有需要的用户进行配对。

边缘网络旨在通过去除中间商服务,提供点对点的计算能力交换,进而减少现有服务器群组对环境的影响。

随着2020年底的到来,分布式劳动力正在成为领域内的新常态。全球范围内对按需计算服务的需求越来越旺盛,而区块链似乎已经准备好迎接这场挑战了。

带你一文看懂 Blockchain + NoSQL数据库

NoSQL数据库和现代区块链分类账都受益于一套共同的原则。由于其二者平台可以相互补充,因此当它们服务于同一应用程序时,能够配合完成多种工作。

在本文中,我们将一起来回顾一下二者协同的重叠部分,学习如何借助Couchbase的NoSQL平台来支持自己的下一个企业分布式账本应用程序(例如利用Hyperledger)。实际上这一话题涉及的内容非常深奥,但在本文中,我们将只触及两个较为浅显的想法,简要勾画出其共性和机遇:分布式计算和世界状态。

%title插图%num

分布式计算

分布式计算是现代企业架构的核心——无论是并行处理CPU/GPU环境、多节点数据库集群,还是在不同地区具有同步集群的全球数据中心。

通过利用这些架构的分布式处理速度、可恢复性和可扩展性(这都是另一个话题了),应用程序开发人员可以专注于构建所需的用户体验,由后端数据系统来完成那些繁重的工作。

“分布式”是什么意思呢?简而言之,就有多个服务器作为节点集群的一部分进行管理。在理想情况下,在分布式系统中是不存在单点故障或集中控制的。

此外,它还假定将工作负载项分解为可供底层原子处理器进行处理的碎片,例如,将工作任务分布到多个节点上。

此类系统的名称中往往包含“点对点网络、集群计算、并行处理”等等。维基百科上有一个很好的列表,里面列出了到目前为止的各类相关系统。

列表地址:

https://en.wikipedia.org/wiki/Distributed_computing?ref=hackernoon.com

%title插图%num

区块链是分布式技术的缩影

尽管分布式技术的优点很多,但其实其应用并没有想象中那么广泛,即使是在现代企业中,系统集中化的现象仍然非常普遍。其中就包括那些设计本应更具弹性的行业,如全球金融系统或供应链管理,实际上,这些行业往往更倾向于选择大型机计算。

顺便提一下,集中式系统非常好辨别,因为当它发生故障时,所有工作全部都会停摆!当所有数据或服务都在一台机器上运行时,一旦出现宕机的情况,系统便会完全停止运作。

这可能是因为启动替代机器需要时间,或者在重新路由用户之前需要时间来识别故障,又或许是因为其他各种毁灭性的工程原因。集中式系统与我们期待的点对点网络正好相反。

然而,随着比特币等平台的出现,下一代数字货币和“分类账”逐渐证明了其可行性。现在,有数千种不同的加密货币和数十种区块链后端都在应用去中心化技术。

此外,请注意“分布式账本”并不等同于许多加密货币中使用的“工作量证明”。相反,我们要把分类账视为已经对更新的应用程序建立起了信任,不必为了添加到链上而进行任何特定的任务。

%title插图%num

分布式NoSQL

同样,企业也在为其内部系统寻找更多利用分布式技术的方法,以减少宕机时间。如果要做一个以分类账为基础的系统的话,目前有多种方法可供选择。如果要做一个通用数据库的话,选项也不少,特别是在数据管理方面。

在运行分布式系统的企业中,大部分技术都能在数据库中找到,尤其是NoSQL平台。这种分布式特性从其出现的*天就成为了Couchbase的支柱之一,填补了传统数据库不可避免的关键性空白。

%title插图%num

Couchbase引入了多维扩展的概念——即能够跨多个节点或多个集群来扩展应用程序和服务。

如果只是在单一节点上运行的话,就和再引入一个JSON文档存储没什么不同了。同样,如果另一种区块链技术不是同样分布在机器集群中的话,也没有任何意义。

集中式系统中一定会存在单一故障点和控制点,从而破坏整个系统的信任。但好在,Couchbase、区块链和相关技术都为分布式系统的存在提供了需求和价值。

%title插图%num

超级账本(Hyperledger)组件

Hyperledger和Couchbase等区块链分布式账本的另一个共同点是,用例显示被管理资产的单一“当前”视图。

据Hyperledger Fabric的说法(一个特定的分布式账本实现),其系统中主要是有两类数据处理组件在发挥作用。

  • 操作事务(Operational transaction)是所有分类帐的核心——它验证、创建并记录分类帐中的所有事务。Hyperledger Fabric处理所有内置权限,确认谁可以发起事务,并将其存储在各种后端技术中。
  • 世界状态(World state)作为另一个主要组件,是另一个维护账户当前状态值的数据视图。当执行由一个实体到另一个实体的事务时,世界状态也会随之更新,保持当前*新值。以前的事务不会被存储在世界状态中。

世界状态系统中的每个帐户都会有单独的值,但是其整体的历史记录会被精确详细地存储在事务系统中。

%title插图%num

NoSQL信息源

以上两个组件都可以通过Couchbase NoSQL数据库作为后端来实现。Couchbase在许多不同用例(金融、欺诈检测、物联网等)中都被用来处理高吞吐量的操作事务。分布式ACID事务也是可以的,这个话题我们将在以后的文章中进行探讨。

将Couchbase作为区块链系统的主要后端数据库可以方便操作与世界状态数据有关的存储/检索。

Couchbase常会被用来存储来自多个不同数据库的数据集合,提供所谓的信息源(Source of truth)。与世界状态类似,它也会存储相关当前数据的物化图像。

因此,Couchbase非常适合需要存储应用程序用户配置文件的应用开发人员。例如,虽然其他的后端系统或许会更新各数据片段,但当用户进行登录时,用户配置文件会立刻出现在JSON文档中。

在这种情况下,使用Couchbase的*终好处是可以享受为开发人员所提供的一切内置优势。一旦数据被投放到数据库中,我们就可以无障碍地访问基于SQL的强大查询工具、使用自然语言进行全文搜索、针对大型数据集进行大规模的大数据分析,等等。这样一来,开发人员便可以专注于产品和用户,同时让后端系统保持管理和同步。

%title插图%num

为什么要选择NoSQL+Blockchain?

我写这篇文章的目的不仅仅是想要比较一下这两种技术,更是希望可以鼓励开发人员和架构师们研究如何将这两种技术结合起来使用,这不失为一种好方法。

Couchbase可以作为任何分布式账本或区块链技术之上的应用开发层,既可以成为可操作的数据库组件,也可以作为世界状态。如果你正在构建的企业解决方案需要快速、便捷地向终端用户提供帐户详细信息,那么世界状态数据库将会是一个*佳的研究用例。

例如,当发生区块链事务且世界状态需要更新时,相同的更新也将被发送到Couchbase供用户使用。由于Couchbase有移动SDK,以及全面的分析SQL支持,因此它能提供比区块链系统更为强大的数据接口。

当我们希望用户能够快速访问*新信息时,这一点就变得尤为重要。

区块链系统需要一定的时间来传递信息,但借助先进的协议,Couchbase能够在更短的时间内实现这一点——在变化发生的*时间便可以建立数据视图。而且由于Couchbase在多集群环境中运行,其平台的稳定性和弹性丝毫不逊于区块链系统。

%title插图%num

集成

虽然目前还没有现成的Couchbase与Hyperledger集成,但Couchbase SDK支持所有主要的编程语言。任何构建基于区块链的分类账的人都可以使用区块链API和Couchbase API,通过JSON将当前世界状态更新发送到NoSQL数据库。

如果你对构建这种集成感兴趣的话,可以看看Hyperledger Fabric中的后端提供商,并从中挑选一个应用于Couchbase Server。

此外,当智能合约应用程序在分类账中更新时,也可以直接在发送的链码中实现该功能。我也只是才开始涉足这方面的内容,但在之后的文章中,我们可以将数据库用户自定义函数(UDFs)和区块链链码进行比较/对比,以供大家参考。

区块链领域还有很多值得我们深入挖掘的东西,但我希望本文中提到的这一点点相似性和重叠点能帮助你梳理自己的认知。

原文链接:

https://hackernoon.com/an-introduction-to-blockchain-nosql-databases-qm2f341y

如何通过 Serverless 轻松识别验证码?

Serverless 概念自被提出就倍受关注,尤其是近些年来 Serverless 焕发出了前所未有的活力,各领域的工程师都在试图将 Serverless 架构与自身工作相结合,以获取到 Serverless 架构所带来的“技术红利”。

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断地登陆尝试。实际上验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。CAPTCHA 的问题由计算机生成并评判,但是这个问题只有人类才能解答,计算机是无法解答的,所以回答出问题的用户就可以被认为是人类。说白了,验证码就是用来验证的码,验证是人访问的还是机器访问的“码”。

那么人工智能领域中的验证码识别与 Serverless 架构会碰撞出哪些火花呢?本文将通过 Serverless 架构和卷积神经网络(CNN)算法,实现验证码识别功能。

%title插图%num

浅谈验证码

验证码的发展,可以说是非常迅速的,从开始的单纯数字验证码,到后来的数字+字母验证码,再到后来的数字+字母+中文的验证码以及图形图像验证码,单纯的验证码素材已经越来越多了。从验证码的形态来看,也是各不相同,输入、点击、拖拽以及短信验证码、语音验证码……

Bilibili 的登录验证码就包括了多种模式,例如滑动滑块进行验证:

%title插图%num

例如,通过依次点击文字进行验证:

%title插图%num

而百度贴吧、知乎、以及 Google 等相关网站的验证码又各不相同,例如选择正着写的文字、选择包括指定物体的图片以及按顺序点击图片中的字符等。

验证码的识别可能会根据验证码的类型而不太一致,当然*简单的验证码可能就是*原始的文字验证码了:

%title插图%num

即便是文字验证码,也是存在很多差异的,例如简单的数字验证码、简单的数字+字母验证码、文字验证码、验证码中包括计算、简单验证码中增加一些干扰成为复杂验证码等。

%title插图%num

验证码识别

1. 简单验证码识别

验证码识别是一个古老的研究领域,简单说就是把图片上的文字转化为文本的过程。*近几年,随着大数据的发展,广大爬虫工程师在对抗反爬策略时,对验证码的识别要求也越来越高。在简单验证码的时代,验证码的识别主要是针对文本验证码,通过图像的切割,对验证码每一部分进行裁剪,然后再对每个裁剪单元进行相似度对比,获得*可能的结果,*后进行拼接,例如将验证码:

%title插图%num

进行二值化等操作:

%title插图%num

完成之后再进行切割:

%title插图%num

切割完成再进行识别,*后进行拼接,这样的做法是,针对每个字符进行识别,相对来说是比较容易的。

但是随着时间的发展,在这种简单验证码逐渐无法满足判断“是人还是机器”的问题时,验证码进行了一次小升级,即验证码上面增加了一些干扰线,或者验证码进行了严重的扭曲,增加了强色块干扰,例如 Dynadot 网站的验证码:

%title插图%num

不仅有图像扭曲重叠,还有干扰线和色块干扰。这个时候想要识别验证码,简单的切割识别就很难获得良好的效果了,这时通过深度学习反而可以获得不错的效果。

2. 基于 CNN 的验证码识别

卷积神经网络(Convolutional Neural Network,简称 CNN),是一种前馈神经网络,人工神经元可以响应周围单元,进行大型图像处理。卷积神经网络包括卷积层和池化层。

%title插图%num

如图所示,左图是传统的神经网络,其基本结构是:输入层、隐含层、输出层。右图则是卷积神经网络,其结构由输入层、输出层、卷积层、池化层、全连接层构成。卷积神经网络其实是神经网络的一种拓展,而事实上从结构上来说,朴素的 CNN 和朴素的 NN 没有任何区别(当然,引入了特殊结构的、复杂的 CNN 会和 NN 有着比较大的区别)。相对于传统神经网络,CNN 在实际效果中让我们的网络参数数量大大地减少,这样我们可以用较少的参数,训练出更加好的模型,典型的事半功倍,而且可以有效地避免过拟合。同样,由于 filter 的参数共享,即使图片进行了一定的平移操作,我们照样可以识别出特征,这叫做 “平移不变性”。因此,模型就更加稳健了。

1)验证码生成

验证码的生成是非常重要的一个步骤,因为这一部分的验证码将会作为我们的训练集和测试集,同时*终我们的模型可以识别什么类型的验证码,也是和这部分有关。

  1. # coding:utf-8
  2. import random
  3. import numpy as np
  4. from PIL import Image
  5. from captcha.image import ImageCaptcha
  6. CAPTCHA_LIST = [eve for eve in “0123456789abcdefghijklmnopqrsruvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ”]
  7. CAPTCHA_LEN = 4  # 验证码长度
  8. CAPTCHA_HEIGHT = 60  # 验证码高度
  9. CAPTCHA_WIDTH = 160  # 验证码宽度
  10. randomCaptchaText = lambda char=CAPTCHA_LIST, size=CAPTCHA_LEN: “”.join([random.choice(char) for _ in range(size)])
  11. def genCaptchaTextImage(width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT, save=None):
  12.     image = ImageCaptcha(width=width, height=height)
  13.     captchaText = randomCaptchaText()
  14.     if save:
  15.         image.write(captchaText, ‘./img/%s.jpg’ % captchaText)
  16.     return captchaText, np.array(Image.open(image.generate(captchaText)))
  17. print(genCaptchaTextImage(save=True))

通过上述代码,可以生成简单的中英文验证码:

%title插图%num

2)模型训练

模型训练的代码如下(部分代码来自网络)。

util.py 文件,主要是一些提取出来的公有方法:

  1. # -*- coding:utf-8 -*-
  2. import numpy as np
  3. from captcha_gen import genCaptchaTextImage
  4. from captcha_gen import CAPTCHA_LIST, CAPTCHA_LEN, CAPTCHA_HEIGHT, CAPTCHA_WIDTH
  5. # 图片转为黑白,3维转1维
  6. convert2Gray = lambda img: np.mean(img, -1if len(img.shape) > 2 else img
  7. # 验证码向量转为文本
  8. vec2Text = lambda vec, captcha_list=CAPTCHA_LIST: .join([captcha_list[int(v)] for v in vec])
  9. def text2Vec(text, captchaLen=CAPTCHA_LEN, captchaList=CAPTCHA_LIST):
  10.     “”
  11.     验证码文本转为向量
  12.     ““”
  13.     vector = np.zeros(captchaLen * len(captchaList))
  14.     for i in range(len(text)):
  15.         vector[captchaList.index(text[i]) + i * len(captchaList)] = 1
  16.     return vector
  17. def getNextBatch(batchCount=60, width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT):
  18.     “”
  19.     获取训练图片组
  20.     ““”
  21.     batchX = np.zeros([batchCount, width * height])
  22.     batchY = np.zeros([batchCount, CAPTCHA_LEN * len(CAPTCHA_LIST)])
  23.     for i in range(batchCount):
  24.         text, image = genCaptchaTextImage()
  25.         image = convert2Gray(image)
  26.         # 将图片数组一维化 同时将文本也对应在两个二维组的同一行
  27.         batchX[i, :] = image.flatten() / 255
  28.         batchY[i, :] = text2Vec(text)
  29.     return batchX, batchY
  30. # print(getNextBatch(batch_count=1))

model_train.py 文件,主要是进行模型训练。在该文件中,定义了模型的基本信息,例如该模型是三层卷积神经网络,原始图像大小是 60*160,在*次卷积后变为 60*160, *池化后变为 30*80;第二次卷积后变为 30*80 ,第二次池化后变为 15*40;第三次卷积后变为  15*40 ,第三次池化后变为7*20。经过三次卷积和池化后,原始图片数据变为 7*20 的平面数据,同时项目在进行训练的时候,每隔 100 次进行一次数据测试,计算一次准确度:

  1. # -*- coding:utf-8 -*-
  2. import tensorflow.compat.v1 as tf
  3. from datetime import datetime
  4. from util import getNextBatch
  5. from captcha_gen import CAPTCHA_HEIGHT, CAPTCHA_WIDTH, CAPTCHA_LEN, CAPTCHA_LIST
  6. tf.compat.v1.disable_eager_execution()
  7. variable = lambda shape, alpha=0.01: tf.Variable(alpha * tf.random_normal(shape))
  8. conv2d = lambda x, w: tf.nn.conv2d(x, w, strides=[1111], padding=‘SAME’)
  9. maxPool2x2 = lambda x: tf.nn.max_pool(x, ksize=[1221], strides=[1221], padding=‘SAME’)
  10. optimizeGraph = lambda y, y_conv: tf.train.AdamOptimizer(1e-3).minimize(
  11.     tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_conv)))
  12. hDrop = lambda image, weight, bias, keepProb: tf.nn.dropout(
  13.     maxPool2x2(tf.nn.relu(conv2d(image, variable(weight, 0.01)) + variable(bias, 0.1))), keepProb)
  14. def cnnGraph(x, keepProb, size, captchaList=CAPTCHA_LIST, captchaLen=CAPTCHA_LEN):
  15.     “”
  16.     三层卷积神经网络
  17.     ““”
  18.     imageHeight, imageWidth = size
  19.     xImage = tf.reshape(x, shape=[-1, imageHeight, imageWidth, 1])
  20.     hDrop1 = hDrop(xImage, [33132], [32], keepProb)
  21.     hDrop2 = hDrop(hDrop1, [333264], [64], keepProb)
  22.     hDrop3 = hDrop(hDrop2, [336464], [64], keepProb)
  23.     # 全连接层
  24.     imageHeight = int(hDrop3.shape[1])
  25.     imageWidth = int(hDrop3.shape[2])
  26.     wFc = variable([imageHeight * imageWidth * 641024], 0.01)  # 上一层有64个神经元 全连接层有1024个神经元
  27.     bFc = variable([1024], 0.1)
  28.     hDrop3Re = tf.reshape(hDrop3, [-1, imageHeight * imageWidth * 64])
  29.     hFc = tf.nn.relu(tf.matmul(hDrop3Re, wFc) + bFc)
  30.     hDropFc = tf.nn.dropout(hFc, keepProb)
  31.     # 输出层
  32.     wOut = variable([1024, len(captchaList) * captchaLen], 0.01)
  33.     bOut = variable([len(captchaList) * captchaLen], 0.1)
  34.     yConv = tf.matmul(hDropFc, wOut) + bOut
  35.     return yConv
  36. def accuracyGraph(y, yConv, width=len(CAPTCHA_LIST), height=CAPTCHA_LEN):
  37.     “”
  38.     偏差计算图,正确值和预测值,计算准确度
  39.     ““”
  40.     maxPredictIdx = tf.argmax(tf.reshape(yConv, [-1, height, width]), 2)
  41.     maxLabelIdx = tf.argmax(tf.reshape(y, [-1, height, width]), 2)
  42.     correct = tf.equal(maxPredictIdx, maxLabelIdx)  # 判断是否相等
  43.     return tf.reduce_mean(tf.cast(correct, tf.float32))
  44. def train(height=CAPTCHA_HEIGHT, width=CAPTCHA_WIDTH, ySize=len(CAPTCHA_LIST) * CAPTCHA_LEN):
  45.     “”
  46.     cnn训练
  47.     ““”
  48.     accRate = 0.95
  49.     x = tf.placeholder(tf.float32, [None, height * width])
  50.     y = tf.placeholder(tf.float32, [None, ySize])
  51.     keepProb = tf.placeholder(tf.float32)
  52.     yConv = cnnGraph(x, keepProb, (height, width))
  53.     optimizer = optimizeGraph(y, yConv)
  54.     accuracy = accuracyGraph(y, yConv)
  55.     saver = tf.train.Saver()
  56.     with tf.Session() as sess:
  57.         sess.run(tf.global_variables_initializer())  # 初始化
  58.         step = 0  # 步数
  59.         while True:
  60.             batchX, batchY = getNextBatch(64)
  61.             sess.run(optimizer, feed_dict={x: batchX, y: batchY, keepProb: 0.75})
  62.             # 每训练一百次测试一次
  63.             if step % 100 == 0:
  64.                 batchXTest, batchYTest = getNextBatch(100)
  65.                 acc = sess.run(accuracy, feed_dict={x: batchXTest, y: batchYTest, keepProb: 1.0})
  66.                 print(datetime.now().strftime(‘%c’), ‘ step:’, step, ‘ accuracy:’, acc)
  67.                 # 准确率满足要求,保存模型
  68.                 if acc > accRate:
  69.                     modelPath = “./model/captcha.model”
  70.                     saver.save(sess, modelPath, global_step=step)
  71.                     accRate += 0.01
  72.                     if accRate > 0.90:
  73.                         break
  74.             step = step + 1
  75. train()

当完成了这部分之后,我们可以通过本地机器对模型进行训练,为了提升训练速度,我将代码中的 accRate 部分设置为:

  1. if accRate > 0.90:
  2.     break

也就是说,当准确率超过 90% 之后,系统就会自动停止,并且保存模型。

接下来可以进行训练:

%title插图%num

训练时间可能会比较长,训练完成之后,可以根据结果绘图,查看随着 Step 的增加,准确率的变化曲线:

%title插图%num

横轴表示训练的 Step,纵轴表示准确率

3. 基于 Serverless 架构的验证码识别

将上面的代码部分进行进一步整合,按照函数计算的规范进行编码:

  1. # -*- coding:utf-8 -*-
  2. # 核心后端服务
  3. import base64
  4. import json
  5. import uuid
  6. import tensorflow as tf
  7. import random
  8. import numpy as np
  9. from PIL import Image
  10. from captcha.image import ImageCaptcha
  11. # Response
  12. class Response:
  13.     def __init__(selfstart_responseresponseerrorCode=None):
  14.         self.start = start_response
  15.         responseBody = {
  16.             ‘Error’: {“Code”: errorCode, “Message”: response},
  17.         } if errorCode else {
  18.             ‘Response’: response
  19.         }
  20.         # 默认增加uuid,便于后期定位
  21.         responseBody[‘ResponseId’] = str(uuid.uuid1())
  22.         print(“Response: “, json.dumps(responseBody))
  23.         self.response = json.dumps(responseBody)
  24.     def __iter__(self):
  25.         status = ‘200’
  26.         response_headers = [(‘Content-type’‘application/json; charset=UTF-8’)]
  27.         self.start(status, response_headers)
  28.         yield self.response.encode(“utf-8”)
  29. CAPTCHA_LIST = [eve for eve in “0123456789abcdefghijklmnopqrsruvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ”]
  30. CAPTCHA_LEN = 4  # 验证码长度
  31. CAPTCHA_HEIGHT = 60  # 验证码高度
  32. CAPTCHA_WIDTH = 160  # 验证码宽度
  33. # 随机字符串
  34. randomStr = lambda num=5“”.join(random.sample(‘abcdefghijklmnopqrstuvwxyz’, num))
  35. randomCaptchaText = lambda char=CAPTCHA_LIST, size=CAPTCHA_LEN: “”.join([random.choice(char) for _ in range(size)])
  36. # 图片转为黑白,3维转1维
  37. convert2Gray = lambda img: np.mean(img, -1if len(img.shape) > 2 else img
  38. # 验证码向量转为文本
  39. vec2Text = lambda vec, captcha_list=CAPTCHA_LIST: .join([captcha_list[int(v)] for v in vec])
  40. variable = lambda shape, alpha=0.01: tf.Variable(alpha * tf.random_normal(shape))
  41. conv2d = lambda x, w: tf.nn.conv2d(x, w, strides=[1111], padding=‘SAME’)
  42. maxPool2x2 = lambda x: tf.nn.max_pool(x, ksize=[1221], strides=[1221], padding=‘SAME’)
  43. optimizeGraph = lambda y, y_conv: tf.train.AdamOptimizer(1e-3).minimize(
  44.     tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=y_conv)))
  45. hDrop = lambda image, weight, bias, keepProb: tf.nn.dropout(
  46.     maxPool2x2(tf.nn.relu(conv2d(image, variable(weight, 0.01)) + variable(bias, 0.1))), keepProb)
  47. def genCaptchaTextImage(width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT, save=None):
  48.     image = ImageCaptcha(width=width, height=height)
  49.     captchaText = randomCaptchaText()
  50.     if save:
  51.         image.write(captchaText, save)
  52.     return captchaText, np.array(Image.open(image.generate(captchaText)))
  53. def text2Vec(text, captcha_len=CAPTCHA_LEN, captcha_list=CAPTCHA_LIST):
  54.     “”
  55.     验证码文本转为向量
  56.     ““”
  57.     vector = np.zeros(captcha_len * len(captcha_list))
  58.     for i in range(len(text)):
  59.         vector[captcha_list.index(text[i]) + i * len(captcha_list)] = 1
  60.     return vector
  61. def getNextBatch(batch_count=60, width=CAPTCHA_WIDTH, height=CAPTCHA_HEIGHT):
  62.     “”
  63.     获取训练图片组
  64.     ““”
  65.     batch_x = np.zeros([batch_count, width * height])
  66.     batch_y = np.zeros([batch_count, CAPTCHA_LEN * len(CAPTCHA_LIST)])
  67.     for i in range(batch_count):
  68.         text, image = genCaptchaTextImage()
  69.         image = convert2Gray(image)
  70.         # 将图片数组一维化 同时将文本也对应在两个二维组的同一行
  71.         batch_x[i, :] = image.flatten() / 255
  72.         batch_y[i, :] = text2Vec(text)
  73.     return batch_x, batch_y
  74. def cnnGraph(x, keepProb, size, captchaList=CAPTCHA_LIST, captchaLen=CAPTCHA_LEN):
  75.     “”
  76.     三层卷积神经网络
  77.     ““”
  78.     imageHeight, imageWidth = size
  79.     xImage = tf.reshape(x, shape=[-1, imageHeight, imageWidth, 1])
  80.     hDrop1 = hDrop(xImage, [33132], [32], keepProb)
  81.     hDrop2 = hDrop(hDrop1, [333264], [64], keepProb)
  82.     hDrop3 = hDrop(hDrop2, [336464], [64], keepProb)
  83.     # 全连接层
  84.     imageHeight = int(hDrop3.shape[1])
  85.     imageWidth = int(hDrop3.shape[2])
  86.     wFc = variable([imageHeight * imageWidth * 641024], 0.01)  # 上一层有64个神经元 全连接层有1024个神经元
  87.     bFc = variable([1024], 0.1)
  88.     hDrop3Re = tf.reshape(hDrop3, [-1, imageHeight * imageWidth * 64])
  89.     hFc = tf.nn.relu(tf.matmul(hDrop3Re, wFc) + bFc)
  90.     hDropFc = tf.nn.dropout(hFc, keepProb)
  91.     # 输出层
  92.     wOut = variable([1024, len(captchaList) * captchaLen], 0.01)
  93.     bOut = variable([len(captchaList) * captchaLen], 0.1)
  94.     yConv = tf.matmul(hDropFc, wOut) + bOut
  95.     return yConv
  96. def captcha2Text(image_list):
  97.     “”
  98.     验证码图片转化为文本
  99.     ““”
  100.     with tf.Session() as sess:
  101.         saver.restore(sess, tf.train.latest_checkpoint(‘model/’))
  102.         predict = tf.argmax(tf.reshape(yConv, [-1, CAPTCHA_LEN, len(CAPTCHA_LIST)]), 2)
  103.         vector_list = sess.run(predict, feed_dict={x: image_list, keepProb: 1})
  104.         vector_list = vector_list.tolist()
  105.         text_list = [vec2Text(vector) for vector in vector_list]
  106.         return text_list
  107. x = tf.placeholder(tf.float32, [None, CAPTCHA_HEIGHT * CAPTCHA_WIDTH])
  108. keepProb = tf.placeholder(tf.float32)
  109. yConv = cnnGraph(x, keepProb, (CAPTCHA_HEIGHT, CAPTCHA_WIDTH))
  110. saver = tf.train.Saver()
  111. def handler(environ, start_response):
  112.     try:
  113.         request_body_size = int(environ.get(‘CONTENT_LENGTH’0))
  114.     except (ValueError):
  115.         request_body_size = 0
  116.     requestBody = json.loads(environ[‘wsgi.input’].read(request_body_size).decode(“utf-8”))
  117.     imageName = randomStr(10)
  118.     imagePath = “/tmp/” + imageName
  119.     print(“requestBody: “, requestBody)
  120.     reqType = requestBody.get(“type”, None)
  121.     if reqType == “get_captcha”:
  122.         genCaptchaTextImage(save=imagePath)
  123.         with open(imagePath, ‘rb’as f:
  124.             data = base64.b64encode(f.read()).decode()
  125.         return Response(start_response, {‘image’: data})
  126.     if reqType == “get_text”:
  127.         # 图片获取
  128.         print(“Get pucture”)
  129.         imageData = base64.b64decode(requestBody[“image”])
  130.         with open(imagePath, ‘wb’as f:
  131.             f.write(imageData)
  132.         # 开始预测
  133.         img = Image.open(imageName)
  134.         img = img.resize((16060), Image.ANTIALIAS)
  135.         img = img.convert(“RGB”)
  136.         img = np.asarray(img)
  137.         image = convert2Gray(img)
  138.         image = image.flatten() / 255
  139.         return Response(start_response, {‘result’: captcha2Text([image])})

在这个函数部分,主要包括两个接口:

• 获取验证码:用户测试使用,生成验证码

• 获取验证码识别结果:用户识别使用,识别验证码

这部分代码,所需要的依赖内容如下:

  1. tensorflow==1.13.1
  2. numpy==1.19.4
  3. scipy==1.5.4
  4. pillow==8.0.1
  5. captcha==0.3

另外,为了更加简单的来体验,提供测试页面,测试页面的后台服务使用 Python Web Bottle 框架:

  1. # -*- coding:utf-8 -*-
  2. import os
  3. import json
  4. from bottle import route, run, static_file, request
  5. import urllib.request
  6. url = “http://” + os.environ.get(“url”)
  7. @route(‘/’)
  8. def index():
  9.     return static_file(“index.html”, root=‘html/’)
  10. @route(‘/get_captcha’)
  11. def getCaptcha():
  12.     data = json.dumps({“type”“get_captcha”}).encode(“utf-8”)
  13.     reqAttr = urllib.request.Request(data=data, url=url)
  14.     return urllib.request.urlopen(reqAttr).read().decode(“utf-8”)
  15. @route(‘/get_captcha_result’, method=‘POST’)
  16. def getCaptcha():
  17.     data = json.dumps({“type”“get_text”“image”: json.loads(request.body.read().decode(“utf-8”))[“image”]}).encode(
  18.         “utf-8”)
  19.     reqAttr = urllib.request.Request(data=data, url=url)
  20.     return urllib.request.urlopen(reqAttr).read().decode(“utf-8”)
  21. run(host=‘0.0.0.0’, debug=False, port=9000)

该后端服务,所需依赖:

  1. bottle==0.12.19

前端页面代码:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4.     <meta charset=“UTF-8”>
  5.     <title>验证码识别测试系统</title>
  6.     <link href=“https://www.bootcss.com/p/layoutit/css/bootstrap-combined.min.css” rel=“stylesheet”>
  7.     <script>
  8.         var image = undefined
  9.         function getCaptcha() {
  10.             const xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(“Microsoft.XMLHTTP”);
  11.             xmlhttp.open(“GET”‘/get_captcha’false);
  12.             xmlhttp.onreadystatechange = function () {
  13.                 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
  14.                     image = JSON.parse(xmlhttp.responseText).Response.image
  15.                     document.getElementById(“captcha”).src = “data:image/png;base64,” + image
  16.                     document.getElementById(“getResult”).style.visibility = ‘visible’
  17.                 }
  18.             }
  19.             xmlhttp.setRequestHeader(“Content-type”“application/json”);
  20.             xmlhttp.send();
  21.         }
  22.         function getCaptchaResult() {
  23.             const xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject(“Microsoft.XMLHTTP”);
  24.             xmlhttp.open(“POST”‘/get_captcha_result’false);
  25.             xmlhttp.onreadystatechange = function () {
  26.                 if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
  27.                     document.getElementById(“result”).innerText = “识别结果:” + JSON.parse(xmlhttp.responseText).Response.result
  28.                 }
  29.             }
  30.             xmlhttp.setRequestHeader(“Content-type”“application/json”);
  31.             xmlhttp.send(JSON.stringify({“image”: image}));
  32.         }
  33.     </script>
  34. </head>
  35. <body>
  36. <div class=”containerfluid” style=”margintop: 10px“>
  37.     <div class=”rowfluid“>
  38.         <div class=”span12“>
  39.             <center>
  40.                 <h3>
  41.                     验证码识别测试系统
  42.                 </h3>
  43.             </center>
  44.         </div>
  45.     </div>
  46.     <div class=”rowfluid“>
  47.         <div class=”span2“>
  48.         </div>
  49.         <div class=”span8“>
  50.             <center>
  51.                 <img src=”” id=”captcha“/>
  52.                 <br><br>
  53.                 <p id=”result“></p>
  54.             </center>
  55.             <fieldset>
  56.                 <legend>操作:</legend>
  57.                 <button class=”btn” onclick=”getCaptcha()”>获取验证码</button>
  58.                 <button class=”btn” onclick=”getCaptchaResult()” id=”getResult” style=”visibilityhidden“>识别验证码
  59.                 </button>
  60.             </fieldset>
  61.         </div>
  62.         <div class=”span2“>
  63.         </div>
  64.     </div>
  65. </div>
  66. </body>
  67. </html>

准备好代码之后,开始编写部署文件:

  1. Global:
  2.   Service:
  3.       Name: ServerlessBook
  4.       Description: Serverless图书案例
  5.       Log: Auto
  6.       Nas: Auto
  7. ServerlessBookCaptchaDemo:
  8.   Component: fc
  9.   Provider: alibaba
  10.   Access: release
  11.   Extends:
  12.     deploy:
  13.       – Hook: s install docker
  14.         Path: ./
  15.         Pre: true
  16.   Properties:
  17.     Region: cn-beijing
  18.     Service: ${Global.Service}
  19.     Function:
  20.       Nameserverless_captcha
  21.       Description: 验证码识别
  22.       CodeUri:
  23.         Src: ./src/backend
  24.         Excludes:
  25.           – src/backend/.fun
  26.           – src/backend/model
  27.       Handlerindex.handler
  28.       Environment:
  29.         – KeyPYTHONUSERBASE
  30.           Value: /mnt/auto/.fun/python
  31.       MemorySize: 3072
  32.       Runtimepython3
  33.       Timeout: 60
  34.       Triggers:
  35.         – NameImageAI
  36.           TypeHTTP
  37.           Parameters:
  38.             AuthTypeANONYMOUS
  39.             Methods:
  40.               – GET
  41.               – POST
  42.               – PUT
  43.             Domains:
  44.               – DomainAuto
  45. ServerlessBookCaptchaWebsiteDemo:
  46.   Componentbottle
  47.   Provideralibaba
  48.   Accessrelease
  49.   Extends:
  50.     deploy:
  51.       – Hookpip3 install –r requirements.txt –t ./
  52.         Path: ./src/website
  53.         Pretrue
  54.   Properties:
  55.     Regioncnbeijing
  56.     CodeUri: ./src/website
  57.     Appindex.py
  58.     Environment:
  59.       – Keyurl
  60.         Value: ${ServerlessBookCaptchaDemo.Output.Triggers[0].Domains[0]}
  61.     Detail:
  62.       Service: ${Global.Service}
  63.       Function:
  64.         Nameserverless_captcha_website

整体的目录结构:

  1.  | – src # 项目目录
  2.  |   | – backend # 项目后端,核心接口
  3.  |       | – index.py # 后端核心代码
  4.  |       | – requirements.txt # 后端核心代码依赖
  5.  |   | – website # 项目前端,便于测试使用
  6.  |       | – html # 项目前端页面
  7.  |           | – index.html # 项目前端页面
  8.  |       | – index.py # 项目前端的后台服务(bottle框架)
  9.  |       | – requirements.txt # 项目前端的后台服务依赖

完成之后,我们可以在项目目录下,进行项目的部署:

  1. s deploy

部署完成之后,打开返回的页面地址:

%title插图%num

点击获取验证码,即可在线生成一个验证码:

%title插图%num

此时点击识别验证码,即可进行验证码识别:

%title插图%num

由于模型在训练的时候,填写的目标准确率是 90%,所以可以认为在海量同类型验证码测试之后,整体的准确率在 90% 左右。

%title插图%num

总结

Serverless 发展迅速,通过 Serverless 做一个验证码识别工具,我觉得这是一个非常酷的事情。在未来的数据采集等工作中,有一个优美的验证码识别工具是非常必要的。当然验证码种类很多,针对不同类型的验证码识别,也是一项非常有挑战性的工作。

%title插图%num

Serverless Devs

Serverless Devs 是一个开源开放的 Serverless 开发者平台,致力于为开发者提供强大的工具链体系。通过该平台,开发者可以一键体验多云 Serverless 产品,*速部署 Serverless 项目。

  • Github 地址:

    https://github.com/serverless-devs

  • Gitee 地址:

    https://gitee.com/organizations/serverless-devs/projects

  • Serverless Devs 官网:

    https://www.serverless-devs.com