图像检索系列——利用 Python 检测图像相似度

本文的代码可在微信公众号「01二进制」后台回复「检测图像相似度」获得。 前言 最近在做一个海量图片检索的项目,可以简单的理解为“以图搜图”,这个功能一开始是搜索引擎带火的,但是后来在电商领域变得非常实用。在制作这个图片检索的项目前,笔者搜索了一些资料,如今项目临近结尾,便在这里做一些简单的分享。本文先介绍图像检索最基础的一部分知识——利用 Python 检测图像相似度。 提到检测“某某”的相似度相信很多人第一想法就是将需要比较的东西构建成两个向量,然后利用余弦相似度来比较两个向量之间的距离,这种方法应用很广泛,例如比较两个用户兴趣的相似度、比较两个文本之间的相似度。但是这个方法在比较图片相似度的时候用到的并不多,原因我之后再说,这里先来介绍下另外两个概念——图像指纹和汉明距离。 图像指纹 图像指纹和人的指纹一样,是身份的象征,而图像指纹简单点来讲,**就是将图像按照一定的哈希算法,经过运算后得出的一组二进制数字。**如下图所示: 在给定的输入的图像中,我们可以使用一个散列函数, 并基于图像视觉上的外观计算它的“图像散列”值,相似的头像,它的散列值应该也是相似的。构建图像指纹的算法被称为感知哈希算法(Perceptual Hash Algorithm)。 汉明距离 通过上述对图像指纹的描述我们知道了可以利用感知哈希算法将图片转换成某种字符串,而比较字符串有一种名为汉明距离的表示方法。以下定义摘自维基百科: 在信息论中,两个等长字符串之间的汉明距离(英语:Hamming distance)是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。 通常用汉明距离来衡量两张图片的差异,汉明距离越小,则代表相似度越高。汉明距离为0,即代表两张图片完全一样。 感知哈希算法 常用的感知哈希算法有三个,分别是平均哈希算法(aHash)、感知哈希算法(pHash)、差异值哈希算法(dHash)。至于这三种哈希算法的介绍和比较很多博客都有写,而且很多库都支持直接计算哈希值,调用一下相关函数就可以了。这里就不多说了,推荐一篇文章👉 《图像相似度中的Hash算法》 代码可在微信公众号「01二进制」后台回复「检测图像相似度」获得 三种哈希算法的实现代码如下: ahash dhash phash 当然,你也可以选择安装ImageHash库,然后调用相应的hash函数来实现计算。 比较两个图片相似度的思路 所以看到这对于比较两张图片的相似度我们就有了一个简单的想法了,只要通过感知哈希算法获得图像的图像指纹,然后比较两个哈希值之间的汉明距离就可以了。 详细的步骤,阮一峰介绍了一个简单的图片搜索原理,可分为下面几步: 缩小尺寸。将图片缩小到 8x8 的尺寸,总共 64 个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。 简化色彩。将缩小后的图片,转为 64 级灰度。也就是说,所有像素点总共只有 64 种颜色。 计算平均值。计算所有 64 个像素的灰度平均值。 比较像素的灰度。将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为 1 ;小于平均值,记为 0。 计算哈希值。将上一步的比较结果,组合在一起,就构成了一个 64 位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。 这种方法对于寻找一模一样的图片是有效的,但是搜索「相似图片」的效果很差,也不能局部搜索,因此通常应用在**「检测图片是否侵权」**上。现在诸如谷歌识图、百度识图几乎都是采用深度学习的方式进行相似性检索,这个下篇文章介绍。 为什么余弦相似度不适合用来检测图片相似度 最后我们来讨论下为什么不使用余弦相似度来检测图片的相似度。开篇我们就说过如果需要用余弦相似度来衡量相似性,我们需要先构造两个向量。通常情况下我们会将图片转化为像素向量(基于像素点灰度值的频次),从而计算两个图片的相似度,这种做法其实就是计算两个图片的直方图的相似度,然而这样就只保留了像素的频次信息,丢掉了像素的位置信息,信息损失太大,只在某些场景下适用。用余弦相似度表示图片相似度的代码同样可以微信公众号「01二进制」后台回复「检测图像相似度」获得。 总结 本文介绍的方法都是通过非深度学习的手段来检测图像的相似度,虽然理解起来都很容易,但是每种方法都有局限性。想要制作一个图像检索系统虽然第一步都是比较图像的相似度,但现如今大多数都是通过深度学习的方法提取出图像特征,然后再进行比较,准确率大大提升。之后我将会讲述如何通过深度学习抽取图像特征的方式来比较图片的相似度。 由于能力有限,在整理描述的过程中难免会有些错误,如有建议,可以留言区批评指正🙏

plainify

图像检索系列——利用深度学习实现以图搜图

文中源码可在微信公众号「01 二进制」后台回复「图像检索」获取。 前言 在上一篇文章《图像检索系列——利用 Python 检测图像相似度》中,我们介绍了一个在图像检索领域非常常用的算法——感知哈希算法。这是一个很简单且快速的算法,其原理在于针对每一张图片都生成一个特定的“指纹”,然后采取一种相似度的度量方式得出两张图片的近似程度。 然而随着深度学习的崛起,极大的推动了图像领域的发展,在提取特征这方面而言,神经网络目前有着不可替代的优势。在上一篇文章中我们也介绍了图像检索往往是基于图像的特征比较,看特征匹配的程度有多少,从而检索出相似度高的图片。而检测图像特征,VGG16 具有得天独厚的优势。 接下来本文将会通过一个简单的案例来实现一个基于深度学习的图像检索小工具。 准备工作 老样子,先来准备好我们此次需要使用到的工具: IDE:Pycharm Python:3.7 Packages:Keras + TensorFlow + Pillow + Numpy keras Keras 是一个高层神经网络 API,Keras 由纯 Python 编写而成并基Tensorflow、Theano以及CNTK后端。简单来说,keras 就是对 TF 等框架的再一次封装,使得使用起来更加方便。 基于 vgg16 网络提取图像特征 我们都知道,vgg 网络在图像领域有着广泛的应用,后续许多层次更深,网络更宽的模型都是基于此扩展的,vgg 网络能很好的提取到图片的有用特征,本次实现是基于 Keras 实现的,提取的是最后一层卷积特征。 思路 主要思路是基于 CVPR2015 的论文《Deep Learning of Binary Hash Codes for Fast Image Retrieval》实现的海量数据下的基于内容图片检索系统。简单说来就是对图片数据库的每张图片抽取特征(一般形式为特征向量),存储于数据库中,对于待检索图片,抽取同样的特征向量,然后并对该向量和数据库中向量的距离(相似度计算),找出最接近的一些特征向量,其对应的图片即为检索结果。如下图所示: 用户请求和预处理部分主要是 Web 服务端应该做的,这里不加以讨论,接下来我们主要进行红线标注部分的实现。 实操 提取图片特征 keras 在其中文文档中提供了一个利用 VGG16 提取特征的 demo from keras.applications.vgg16 import VGG16 from keras.preprocessing import image from keras.applications.vgg16 import preprocess_input import numpy as np model = VGG16(weights='imagenet', include_top=False) img_path = 'elephant.jpg' img = image.load_img(img_path, target_size=(224, 224)) x = image.img_to_array(img) x = np.expand_dims(x, axis=0) x = preprocess_input(x) features = model.predict(x) 这里我们需要对其进行简单修改,封装成一个类以便后期调用。如下图所示: ...

在 Flutter 中使用 WebView

本文示例代码可在微信公众号「01二进制」后台回复「WebView」查看下载 前言 我们知道在开发 Native App 时经常会有打开网页的需求,可供的选择通常只有两种: 在 App 内部打开网页 通过调用系统自带浏览器打开网页 以「微信」举例,我们在微信内阅读公众号的时候就是第一种情况,但是微信同时也提供了Open with Browser 这一选项,这就是第二种情况了。 简单的介绍下 Android 中的 WebView 想实现第一种效果,我们需要使用一个名为 WebView 的东西,先来看看在 Android 中如何实现一个 WebView 吧。 在 Android 中我们需要先在一个 Layout 中放入 WebView 这个控件,然后在对应的 Activity 或者 Fragment 或者各种 Custom View 中执行一个个的 findViewById…… 额,Android 开发者一定知道我在说什么(真的很麻烦) WebView in Flutter Flutter 的 WebView 出现已经有一段时间了,在 Flutter 插件社区官网搜索 WebView 即可搜索到比较流行的插件,如下图所示: 其中 webview_flutter 是官方维护的 WebView 插件,特性是基于原生和 Flutter SDK 封装,继承 StatefulWidget,因此支持内嵌于 flutter Widget 树中,这是比较灵活的; ...

在腾讯云 Ubuntu18.04 安装配置 MySQL 5.7(踩坑警告⚠️)

前言 和标题一样,本文的主要内容就是在腾讯云 Ubuntu18.04 上安装配置 MySQL 5.7,之所以要写这篇文章是因为前两天和朋友讨论现在的 mysql 设置 root 账户的密码上和以前不一样了而且后续的操作也比以前麻烦了,他不信相信,然后为了向他验证我的说法,我就把我的一台暂时闲置的腾讯云服务器重装了下系统(程序员较起真来就是这么不讲理 😜) 这不,虽然我证实了我说的,但我也付出了要重新配置我这台服务器的惨痛代价,既然这样倒不如把安装配置的过程记录下。话不多说,我们就开始吧。 环境准备 一台腾讯云服务器 系统环境为 Ubuntu 18.04 安装 安装 mysql 非常简单,只需要执行下面两个命令: sudo apt-get install mysql-server sudo apt-get install mysql-client 检查 MySQL 是否运行: sudo netstat -tap | grep mysql 如果成功安装,我的会显示如下内容: tcp6 0 0 [::]:1030 [::]:* LISTEN 5743/mysqld 顺便在这里提一下 **重启/打开/关闭 MySQL ** 的方法是: sudo service mysql restart/start/stop 配置 虽然我们可以通过执行两个命令就能很方便的安装好 MySQL,但是我在安装过程中并没有出现要我写用户名和密码的地方,这让我一脸懵逼,索性在终端输入mysql -u root -p之后,要求我输入密码,可是我并不知道密码,心想可能默认密码就是空吧,直接回车不对,随便输入一个密码也不对,终于在查找了很多资料后,我找到了解决方案。 查看初始用户名和密码 查看一个文件 sudo cat /etc/mysql/debian.cnf 在这个文件里面有着 MySQL 默认的用户名和用户密码, 最最重要的是:用户名默认的不是 root,而是 debian-sys-maint,如下所示 # Automatically generated for Debian scripts. DO NOT TOUCH! [client] host = localhost user = debian-sys-maint password = skFz7zS0Fl1t2QHK socket = /var/run/mysqld/mysqld.sock [mysql_upgrade] host = localhost user = debian-sys-maint password = skFz7zS0Fl1t2QHK socket = /var/run/mysqld/mysqld.sock 记下这里的 user 和 password,然后到终端里输入 mysql -u debian-sys-maint -p ,随即会让我们输入密码,此时输入我们刚才记下的密码即可进入 mysql 的 shell 环境了。 ...

大模型让你焦虑了吗?

之所以会起这个标题,源自于前段时间笔者的亲身经历,上个月我利用ZhipuAI结合开发了一个阅读PDF的AI小工具,还没开始正式使用,突然发现KimiChat已经支持20万字的超长文本输入,意味着你可以直接塞入一整套知识库进去。再回头看看自己写的那个工具,才越发觉得所做的一些努力不值一提。 这不禁引发我的思考,移动互联网时代,个人开发者以极低的成本(一台电脑,一部手机)开发出独立作品的场景,在如今的大模型的时代变得不再现实。那个年代,代码稀少,因此程序员的薪资才水涨船高。可生成式AI的出现,让代码生成的成本变得更低,代码在这个时代变得越来越不值钱。 你问我焦虑不焦虑,那肯定是焦虑的,真的很焦虑。尤其在对比了国内外的现况后,变得愈发焦虑了。似乎海外一直在高歌猛进,Google、Meta、OpenAI等大公司军备竞赛,NVIDIA股票节节攀升,各种开源工具应接不暇。反观这里,仍然一直发布着各项评测结果遥遥领先,但实际效果一般的大模型,颇有一种被世界甩在身后的感觉。 笔者常年在互联网上划水,算是比较先接触AIGC和大模型的,期间也陆陆续续写过一些文章。从一年多的使用来看,大模型真的很强,强到帮我写代码,根据大纲编辑文字,给我提供各种思路和解决方案。但同时大模型也很弱,弱到我问他鲁迅和周树人是什么关系都会瞎说。这种矛盾带来的割裂感让我陷入了深思。技术,尤其是像生成式AI这样突破性的技术,始终带有双刃剑的特质。其一面能够无限放大人类的创造力,简化日常工作,提升效率;另一面则是误导信息、扩张偏见,让你无法分辨究竟什么是真,什么是假。因为大模型对于输出错误答案和输出正确答案同等自信,以至于在某种程度上反而增加了人类获取真实信息的成本。 大模型时代的开源 借着这个话题,探讨一下如今这个时代大模型开源是否还有意义。 首先,开源作为一种协作模式,是鼓励开发者共享知识和资源,这样可以加速技术的迭代和创新,同时也降低了参与门槛,使得更多的企业和个人能够参与到AI大模型的研发和应用中来,颇有一种众人拾柴火焰高的意思在里面。但是现在,我们发现各种性能卓越的大模型都是闭源产品,无论是GPT4,还是Gemma,都是大公司的产品。OpenAI在成立之初,是希望让AI服务于每个人,但是现在越来越多的人调侃他们变成了CloseAI。 闭源可以保护商业模式,聚集人才和算力资源,反观开源社区的很多成员都是个人开发者,没有足够的算力支持,所以观察GitHub可以发现一个规律,大公司不断发布自己最新最强的大模型,个人开发者不断的发布自己的各种调用大模型API的应用和工具。 也许未来开源社区会成为反哺这些大模型公司生态重要的一环。 大模型之于我们 这几天腾讯新闻有两篇文章值得看一看。分别是《朱啸虎讲了一个中国现实主义AIGC故事》和《月之暗面杨植麟复盘大模型创业这一年:向延绵而未知的雪山前进》 这两篇文章介绍了中国科技界对大模型的态度分裂成两股阵营,一方认为应该追求更大更强的AI能力,另一方认为应该将足够的AI能力投入可以快速变现的商业场景中。 其实,杨植麟和朱啸虎并不是对立的。在中国也可以做大模型,但是要基于两个真相。第一,阿里这类云计算厂家会砸钱,反正大模型买我的算力,甚至投资款都用算力支付,云计算规模上去还可以打价格战,一石多鸟,空手套白狼。第二,大模型作为基础设施,中国肯定要自主可控,不管好不好,肯定要有。 这些机会,属于杨植麟这样的,和杨立昆一起发过paper的天之骄子。而普通人做AI,多听听朱啸虎说的,不要挑战高大上的科研,一定要做应用,一定要想办法快赚钱,养活自己。 套用《一代宗师》的台词 —— 今天我们不比武功,比想法。 那么未来AI的走向会是什么样的呢? 悲观的预测,这轮AI是元宇宙,泡沫破裂,英伟达成为思科。又或者这轮美国的AI,成了中国的5G,的确遥遥领先,但是烧了那么多钱,却找不到爆款应用。中性的预测,这轮AI是互联网。前期技术曲线陡峭,后期变得平缓,中国能慢慢赶上来。再次印证,美国擅长底层技术,中国擅长应用落地。最乐观的预测,这轮AI是AGI,scaling law带来的强人工智能,是黑客帝国,是人类被全面超越和奴役的开始。 但是不管怎么样,我们都不应该焦虑,饭是一口一口吃的,路也是一步一步走的,我们注定不是会引领世界的一批人,因此让自己站在巨人的肩膀上才是我们要做的。历史课本上说人类进化的重要标志是学会使用工具,现如今大模型是进入信息时代以来,人类最伟大的工具,你能够直接调用人类千年以来积累的知识与技能,这些足以让你变得十分强大。

plainify

好久不见

今天登录公众号后台看了一下,上一篇文章已经是 2 个月前写的了。 其他平台也基本属于停更状态,这段时间一直在忙毕业设计和论文的事情,属实有些耽搁了,不过今天刚好把毕业论文提交了,毕业的倒计时也开始了。想着从下周开始,我就准备恢复更新了。今天就先水一篇,讲讲最近停更期间发生的一些事情吧。 毕业设计和论文 熟悉我的读者应该知道我现在还没毕业,今年 6 月份才能拿到我的硕士毕业证。总体上来看,我读研的这两年还是比较快乐的,科研的压力虽然有一些,但因为是推免的进实验室比较早所以也就提前适应了。研一的课程虽然比较多但是难度适中,再加上去年碰到了疫情,很多时间都是在家中度过,之后暑期去实习了几个月就回学校了,研二因为实验室已经来新人了相对就轻松了些,等之后找个时间写一篇文章专门聊聊这几年读研的生活。 其实读研期间我没有做出什么成果,主要还是因为自己的想法就是找工作,不想花那么多时间浪费在一些无关紧要的事情上,所以这个毕设可以算是我读研期间为数不多的成果之一了。大体框架如下: 和大多数软件系学生类似,我的毕设也是一个 XXX App,用的技术也是很常见的一些技术,Flutter、Spring Cloud、Docker、MQ 这些,但是涉及的领域是我比较喜欢也比较看好的智能家居领域。 我自己是一个智能家居的爱好者,也爱好捣鼓,所以在家里也购买了一些小米智能家居和苹果智能家居的设备,读研的时候恰好学长就给我安排了一些和智能家居有关的活,虽然活不是很多,难度也还好,但因为自己喜欢也就坚持着做了,所以毕设也就恰好选择了这个方向。做下来这段时间,总体感受还是很不错的,如果有学弟学妹读研之后不知道选什么方向的,这个领域可以涉及下,还是挺好玩的。 多平台短视频发布工具 MediaPub 其实这两个月里,除了在忙活毕业设计和毕业论文,我还在捣鼓另一个东西,也就是一个多平台短视频发布工具 MeidaPub,做这个工具的初衷就是实现一个可以一键将短视频发布到多个平台的工具,提高一些视频制作者的发布效率,其实已经开始内测了,之前也贴出了官网地址和使用说明,目前看来用户不是很多,可能是跟宣传有关吧,不过好在还是收集到了一些测试反馈,之后我也将会持续更新这个软件。 其实最近也在忙活着给他来一个大版本的更新,未来可能也会把图文多平台发布也加进去,有兴趣的小伙伴也可以联系我一起搞点事情。 最后 简单聊了聊最近停更期间做的一些事,接下来要忙活的应该就是准备毕业答辩和旅行了,公众号文章我也会持续更新下去,还有就是之前一直说的要写的 Spring Boot 入门教程,虽然中途写到了第二章就停更了,不过近期也会慢慢拾起的,最后希望接下来这段时间可以顺利的从科研狗 🐶 过渡到打工人 👨‍🔧。

plainify

如何一步步 get 大厂前端 offer,你也许可以参考这份成长经历。

之前在《前端菜鸟的阿里实习百日之旅》一文中,我的好友「承和」分享了一些作为前端开发实习生的感悟,文章发出后,很多人在后台询问能不能谈谈前端的学习路径,以及作为一个萌新如何拿到大厂的前端 offer。的确,秋招已过去大半,下一波待就业的应届生们也可以开始考虑实习和春招了,为此,本文以 Q&A 形式邀请了他来讲述他是如何一步步 get 大厂的前端 offer,希望他的成长经历可以为正在准备的人带来一些启发。 Q:初来乍到,先做个简单的自我介绍吧 「01 二进制」的读者,你们好,我是承和,目前是一名计算机专业的研三学生,就读于杭州电子科技大学,本科就读于马爸爸的母校,也就是杭州师范大学。在此次秋招中,很幸运的拿到了阿里,字节,拼多多等公司的 Offer,希望我的成长经历能对你们有所启发。 Q:能不能简单说说你这些年的前端学习经历呢? 说起前端,其实我最早接触的是 iOS 客户端开发。在我大二的时候,苹果发布了最新的开发语言 Swift,恰巧在当时,我在编程上的启蒙老师所在的实验室正在招新,听说加入还会分配 MacBook,于是我马上联系了他。就这样我顺理成章的白嫖到了一台 MacBook Air 笔记本,而当时分配到的任务是开发一款 App,也正是从这个任务开始,我走上了软件开发这条不归路。 后来实验室为了减少开发和维护的成本,导师让我学习有关跨平台应用开发技术,也正是从那时起,我逐渐接触到前端开发。在学习过程中,我发现,相对于客户端,前端开发有更广的发展空间,再加上当时客户端的就业形势是**“49 年入国军”**,因此,毅然决然地选择了前端开发。 再后来,读研期间,学习了点深度学习的相关知识,发现这玩意儿极其烧脑,加上国内学术圈又相当浮躁,多数研究生基本都是为了发论文而发论文,很少有能实际落地应用到工程之中的。加之现在算法岗 hc 非常少,大厂的算法岗几乎是神仙打架,想着肯定是没办法靠算法吃饭了,所以又重新投入到了前端的怀抱中,从 0 开始学起,好好沉淀自己的前端技术。 Q:你这也算是有了几年开发经验的老鸟了,要不简单谈谈你是如何学习前端技术的? 我个人认为,学习编程就和练武一样,学习任何一门技术都是修炼内功和学习招式的过程。内功指的就是基础,就前端领域而言,也就是我们常说的前端三板斧:HTML、CSS 和 JS。我们可以根据网上较流行的知识图谱或者一个面试宝典,进行初步的学习。若想要深刻了解的话,便要通过阅读大量相关的专业书籍来加强理解(后面我也会推荐一些,此处没有广告,可放心食用)。 招式指的便是各种前端框架,这些框架帮助我们封装了底层对于 dom 的操作等,使我们能够专注于业务代码的编写。现如今国内 Vue 和 React 大行其道,但是作为 JS 革命性的框架之一,jQuery 我们自然不能忘记,该框架非常适合前端入门者进行学习。 对于框架的学习大致可以分为以下 3 个步骤 👇: 第一步,学会**招式的使用,**你要学会怎么用它,知道这个框架究竟解决了哪些问题,这些资料最好的获取方式便是官网,例如 React 官网,便清楚的说明了 React 的用途,在开发中大多数遇到的问题也能在 React 官网上找到解决方法。 第二步便是用框架做一个项目,在编写项目的过程中,你会遇到很多"稀奇古怪"的问题,通过解决这些问题,可以加深你对框架的理解。 第三步要做到知其然知其所以然,在熟练掌握框架的使用后,去学习它的源码,去看一些源码解析或者大佬的直播课,最好是自己手动实现一个类似于 React 的 diff 算法。 Q:在学习的过程中,有什么需要注意的呢? 在学习过程中你会接触到非常多的知识点,难免会产生焦虑,这时候要做的就是定义一个边界,做到对另一个知识点的探索适可而止。 例如,在利用 React 脚手架的开发过程当中,我们会接触到 Webpack,我们可以先用脚手架中 Webpack 默认配置来进行项目开发,去了解 Webpack 的功能和大致打包流程,来做到对 Webpack 的整体认识,在后续进行项目优化时,可以尝试对默认配置进行修改,通过熟读 Webpack 官网,了解针对 Webpack,我们有哪些优化手段,并且付诸于实践,在工程当中加深自己对于框架和工具的理解。 ...

plainify

如何设计一个积分领取系统

积分作为一种营销手段,被广泛运用于线上/线下的产品中,以此来增加用户对于产品的粘性。比如天猫积分可以用来兑换商品,京豆可以在下单折扣等,如下图所示。 如今,随着获客成本的增加,如何减少用户的流失,变成了各个产品的核心命题之一。也正因如此,很多业务引入了各式各样的积分系统。为了更好的面对业务带来的变化,对整个积分兑换流程做一个合理的抽象是正确且有必要的。只是,整个积分的兑换是一个非常庞大且复杂的流程,因此,本着一切从简的理念,我们这期先聊一聊如何设计一个积分领取系统。 从具体到抽象 无论是天猫积分/京豆,都会有一个规则说明,笼统的来说,无外乎两个主要的功能点:如何获取积分以及如何消费积分。 获取京东的方式通常有: 每天签到一次,领5个京豆 买100元以上的东西,领20个京豆 发1条20字以上的评论,领10个京豆 …… 从上面的规则中,我们可以看出,基本符合一个格式:xx行为,执行yy次,可以得到zz个ww。 于是我们可以将上述案例抽象成以下三步: 行为感知 任务推进 权益领取 架构设计 本着高内聚与低耦合的理念,我们可以抽象成几个核心模块 行为感知模块 该模块负责对用户的行为进行感知,如果用户的登录行为、点赞行为、下单行为等。通常情况,该模块是异步进行的。 原因也很简单,行为感知作为依附于主链路存在的功能,其存在不应该影响到主链路的运行。 例如“用户每日登录获取一个积分”这样的案例,我们不应该在登录接口同步进行行为感知。通常采取的做法是,开一个子线程调用一下行为感知接口即可。 但是,行为的种类千奇百怪,如何对行为进行一个合理的抽象便是我们要考虑的问题了。一个简易的流程图如下所示: 在上述流程里,行为感知模块可以感知多种数据源(消息),解析不同业务的消息数据后,为了转换成系统内部需要的行为对象,通常还需要有一步数据填充的过程,这一步需要我们和其他的服务进行联动的,在数据填充完成后,外部的行为才真正变成我们整个积分领取系统内部所需要行为对象,这一步可以可以再发出一个行为变更消息出去,以便和其他业务解耦(当然也可以提供一个接口)。 任务推进模块 我们将如何领取积分归到该模块中。 所谓任务推进,便是我们上述案例中提到的例如:下单20元的物品、点赞10条内容、登录1次这样的行为。在该模块中,我们需要维护一张用户行为记录表和一个任务规则表。 行为记录表负责维护用户每天的任务进度状态,例如点赞了多少条内容等信息。 任务规则表负责录入玩法规则,比如点赞10条内容可得一个积分这样的信息。该部分通常会由一个运营后台来承接。 一个简易的流程图如下 在上述流程中,任务的匹配与任务进度的更新是比较核心的部分。如何做好技术选型是比较重要的一点,我们将会在下一章详细介绍。 权益领取模块 出于高内聚低耦合的思想,我们可以将权益系统的职责设置的简单一些,只需要负责增加积分、减少积分,查询积分明细这几个工作。至于从什么渠道加积分、加多少积分,则将职责上移到任务推进模块中进行。 举个例子解释一下。比如,用户通过下订单赚取积分。订单系统通过异步发送消息或者同步调用接口的方式,告知行为感知模块订单交易成功,补全必要的信息后,发送给任务推进模块;任务推进模块根据拿到的订单信息,查询订单对应的积分兑换规则(兑换比例、有效期等),计算得到订单可兑换的积分数量,然后调用权益领取模块的接口给用户增加积分。 这一部分的流程比较简单,就不画图了。但是如何保证积分加的正确,如何保证不重复加或者不漏加则是该模块的难点。 小结一下 简单对上述三个模块进行一个小结。有了上述共识后,我们可以抽象出如下的功能图。 详细设计(数据表设计) 数据库和接口的设计非常重要,一旦设计好并投入使用之后,这两部分都不能轻易改动。 改动数据库表结构,需要涉及数据的迁移和适配。改动接口,需要推动接口的使用者作相应的代码修改。这两种情况,即便是微小的改动,执行起来都会非常麻烦。因此,我们在设计接口和数据库的时候,一定要多花点心思和时间,切不可过于随意。 相反,业务逻辑代码侧重内部实现,不涉及被外部依赖的接口,也不包含持久化的数据,所以对改动的容忍性更大。 在对上述流程进行了一个抽象之后,我们发现有两个非常重要的表:任务规则表和任务进度明细表。 数据字典设计 任务规则表 这张表的设计比较简单,只需要指明相应的活动规则即可,表结构如下。 字段 说明 id 活动ID name 任务名称 description 任务描述 type 任务类型 userAction 用户行为 num 任务次数 rewardCount 任务完成后的奖励数值 rewardType 任务完成后的奖励类型 period 单次任务的周期,本次需求仅支持天级别(DAY)的周期 threshold 每个任务周期内能完成的次数上限 🌰场景举例 ...

plainify

对 Node 作为系统架构中间层的一些想法

Web 发展三部曲 青铜时代 在互联网诞生之初,网页还只是一个承载静态信息的载具,只能显示一些纯静态的文本和图片。这种静态页面不能读取后台数据库中的数据,是一个完全封闭的生态,我们姑且称这是 Web 发展的“青铜时代”。 白银时代 而为了使 Web 更加充满活力,开发者们一次又一次的对动态网页这一高地发起进攻,主要目标是允许网络开发人员快速编写动态页面。这一个阶段,以 PHP、JSP、ASP.NET 为代表的动态页面技术相继诞生。在这些动态页面技术面前,网页不再是静止的,可以根据不同的人,不同的地域,不同的时间段呈现出不同的数据结果,从这时开始,Web 发展进入了其“白银时代”。 黄金时代??? 随后,各种各样的网站如雨后春笋般出现,网站的复杂程度也呈爆炸式增长,程序员既绘制页面又控制业务逻辑的难度也越来越大,这时,前后端分离的概念被提出来了。核心理念是**「让专业的人做专业的事」**。于是,程序员的职业发展来到了一个分叉点,是选择专门绘制页面的程序员还是专门控制业务逻辑的程序员成为了一个争论点,直至今日。 在这个阶段,前端是完全不需要参与后台的数据处理,他只需要利用约定好的接口拿到合适的数据,然后渲染页面即可。而且这么做的一个好处是,开发进度不会堵塞,后端开发不需要等待前端完成之后才能继续,只要 Mock 完整数据之后前后端联调即可。 前后端分离的挑战 那这个阶段,是 Web 发展的“黄金时代”吗? 私以为不是。 前后端分离是一个非常好的思想,让专业的人做专业的事情这一美好愿景,在实际的过程中却受到了很多挑战。 举个例子,前端的接口通常是按照逻辑来展现数据的,有时候为了提高效率,后端会根据前端需要的数据结构做数据封装。这就意味着后端还是做了 view 层的工作,违背了前后端分离的初衷。 Web 系统架构的中间层 为了解决上述问题,有人提议干脆把 Controller 和 View 的工作都转移到前端,后端只负责处理 Model 的数据与底层逻辑。于是 Node 中间层这个解决方案就被提出来了,这种方案好不好我们暂且按下不表,先来说说这一个中间层的职能是什么以及架构是什么样的。 中间层架构 其实中间层要做的事很简单。 原来客户端直接向 Server 发送请求,Server 层收到请求后经过计算处理将结果返回给浏览器。如今浏览器将请求发送给 node 层,node 层经过一轮处理后再向 Server 层发起请求。Server 层处理完毕将响应结果返回给 node 层,node 层最后将数据返回给浏览器。因为 node 层的出现,Server 层可以只用关注业务本身,而不必理会前端对字段的特殊要求。 而且,由于增加了 NodeJS 层,每种前端的界面展示逻辑由 NodeJS 层自己维护。产品经理在中途如果想要改动界面,可以由前端自己维护,无需后端操心。前后端各司其职,后端专注于自己的业务逻辑开发,前端专注于产品效果开发。这样就实现了更彻底的前后端分离。 乍一看是不是觉得这个方案很完美呢?但事实真的是这样吗? 太阳底下无新事 细心留意文章里的图,就会发现整个系统越来越复杂,系统复杂会带来很多问题,比如人员沟通成本增加,系统整体性能下降,安全隐患增加等等。这些都是老生常谈的,只要是增加系统的层级就一定会出现这些问题,还有什么其他的问题吗? 我们先思考一个事情,这些东西是不是非 Node 不能做? ...

小王子——爱与责任

因为想锻炼自己的英文能力,所以开始尝试阅读英文书,第一本便是这本小王子,其实选它的原因很简单,一是自己之前读过,大致情节都还记得些;二是因为小王子里面的词汇比较简单,阅读起来的压力会小一些。 第一次看的时候觉得内容蛮无厘头的,可能是因为自己当初太年轻,经历的事情太少了吧,再读这本书的时候,才渐渐明白小王子、玫瑰和狐狸都扮演着什么角色。曾经我也很好奇为什么小王子明明已经来到了地球,却仍然心心念他那个星球上的玫瑰。直到后来经历了和初恋分手,才渐渐明白,正因为小王子与玫瑰,彼此驯化,彼此需要,小王子和玫瑰对彼此来说才都是独一无二的,哪怕聪明如狐狸,也无法替代。 恋人该是如此,我与千千万万人中遇见你,本是沧海一粟,但正因为为你花过心思,付出真心,彼此陪伴,我们才变成彼此间心目中的独一无二。 扯的有点远了,整本书有很多金句,但是我最喜欢的是下面这句: What is a rite? Those also are actions too often neglected. They are what make one day different from other days, one hour from other hours. 仪式是什么?它就是使某一天与其他日子不同,使某一时刻与其他时刻不同。 生活还是多点仪式感的好,因为它代表你对生活的态度,这种态度,会让你活的更高级。简单说,就是会让你过的更幸福。 就像王小波说的:一个人只拥有此生此世是不够的,他还应该拥有诗意的世界。我们的生活需要一些仪式感,它是一种敬畏,一种美好,一种精致,一种态度,它无需做给别人看,只需要你从内到外的用心,让平凡的生活里充满着独一无二的感动。 希望自己可以以自己喜欢的方式过一生,不愧对别人,亦不辜负自己。