从零开始:Windows网关+RabbitMQ+Linux+MySQL的全链路实践)
前言项目的背景来自于个人对于未来的职业规划和一次偶然的头脑风暴大概三个月多前我开始学习Android和Kotlin目的是为了让之前的一个个人项目完成从PC端到移动端的迁移。在学习过程中由于条件的限制我没有足够的设备进行完整的APK分发和APP实机测试流程于是我转而投向了电脑上的各类模拟器。由于模拟器本身可以看作一个可交互虚拟机因此我选择在其中进行相关的APP调试。而调试的过程中由于个人的职业规划偏向后端所以我开始尝试改变其中简单的JSON临时储存改用MySQL这类专业数据库。MySQL作为数据库传统的图形化界面比如Windows虽然有许多类似DataGrip的辅助工具但是我个人还是喜欢使用自动化的脚本和命令行难以有效的进行维护和开发于是作为后端的重要技术之一Linux的学习也就自然提上了日程并使用了VMwareWorkstationPro运行Linux虚拟机。如上我在对于MySQL练习APK分发模拟Linux虚拟机配置的过程中产生了一个构想Windows这类操作系统中拥有丰富的可用端口结合之前我接触到的本地域名和HTML知识是否可以通过多个虚拟机在单台宿主机上的运行借助不同的端口和本地域名模拟一个较为完整的通讯模型于是项目的设计开始了。经过暑假对各种知识的学习基础的设计架构得以不断迭代到一个理论可行的版本。现在我将开始对项目逐步实践并进行总结作为个人的技术记录的同时也发布到网络上供人避免实践过程的各种错误或者进行可能的借鉴。一.关于Linux和虚拟环境对于我来说配置Linux虚拟机和最开始的使用部分还是让我花了一些时间从VMware本身的下载到对于虚拟机内容的逐步熟悉再到对于Linux的具体操作中间有不少问题在这里我会挑选几个进行详细描述。1.关于Linux命令行Linux提供了丰富的系统指令供我们进行具体的操作由于内容过多我在此仅进行一小部分的讨论用以记录个人对Linux命令行的学习和理解以常见且实用的更新指令为例子在执行sudo apt update 代码后会出现类似的部分输出其中我们发现tsinghua这个镜像站出现在了我们的update队列中对于Windows用户来说使用powershell或者CMD进行命令行式的软件下载时往往需要我们进人工的path和域名指定才能顺利避开使用官方网址带来的严重延迟或者根本链接不上的情况如果拿我个人距离就是在使用AndroidStudio时对于gradle的下载在没有指定对应的国内镜像站或者使用一些VPN的情况下会变得非常困难。幸运的是Linux给出了它自己的解——由于目前的发行版众多维护一个类似于Google Store那种的庞大平台是很难的因此在发行版运营自己的仓库的同时各个公司高校科研单位等会搭建镜像站比如我们现在看到的tsinghua就是在国内比较流行的一个其余还有alitencent中科大等这里不多赘述。Linux会根据我们所在的地理位置获取最近的镜像服务器在/etc/apt/sources.list文件中写入一个或多个镜像站地址。然后尝试获取内容如果失败就会换到下一个网址。同样的我们在install相应的软件时也遵循类似逻辑 所以Linux在这里确实给了我相对于Windows最直观的体验提升 。2.关于文本编辑器和双端开发作为Linux上的主流编辑器Vim和nano各有优劣由于我是新手我最开始选择了nano。通过使用如下命令我们得以进行文本的编辑cd /mnt/hgfs/publicfiles/scripts nano producer.py如你所见我们现在进入了脚本文件的内部并可以进行对于文本的编辑具体的使用我不展开但是nano本身的使用却有个问题python作为一个需要严格控制缩进的语言对于文本格式有较高的要求。在最开始的几次编写中由于nano对于自动缩进功能的一些缺陷和我个人的能力问题我在运行时需要频发的修改.py脚本这让我非常烦躁。于是我选择使用Windows宿主机上的PyCharm结合之前我在学习python时留下的一些环境进行对脚本的开发。当然这会带来一个问题——如何将脚本文件进行双端共享幸运的是VMware为我们提前解决了问题通过VMware提供的共享文件夹功能我们得以轻松的实现双端开发当然还有一些别的路径比如远程开发这里选择使用publicfiles是因为当前项目体量较小而且操作较为方便。所以我们现在可以同时享受Linux的脚本自动化操作和Windows上IDE带来的舒适开发环境。3.关于Linux上的python环境当我在Linux上尝试编写并运行python脚本时有一个问题就很自然的出现了Linux本身自带python作为系统工具用来运行诸如前文提到的apt等指令但是对于我们具体的开发还需要进行虚拟环境也就是venv的搭建。我们可以用pip3指令或者pip进行对于对应包的安装安装应当包含Windows和Linux两端。pip install flask pika requests mysql-connector-python ##Windows/PyCharm pip3 install pika mysql-connector-python ##Linux之后就需要对venv进行创建需要注意的问题在于venv和terminal之间的关系。在实际的开发过程中我需要开启多个终端但是每个终端对应的虚拟环境需要单独激活。所以为了避免项目和环境之间关系的错乱我建议对不同的目录创建不同的环境并进行有效保存当然这也是venv的主要目的之一但作为新手我确实被venv的管理混乱折磨过所以在这里做个提醒。python3 -m venv myenv ##创建名为myenv的虚拟环境 source myenv/bin/activate ##激活二.关于MySQL和RabbitMQ在当前的后端开发或者说整个计算机行业来说就我个人体会消息队列和数据库是每个人的必修课学习实践工作或多或少都应该会用到相关知识。对该项目来说MySQL和RabbitMQ本身的操作其实并不算过于复杂至少是可以接受的主要的问题在于如何将它们融入一个更大的项目中同时确保它们得到正确且高效的使用。虽然真实的开发环境会更加复杂但即使是当前的模拟环境仍会带来许多对我这个初学者来说很陌生的知识下面挑选几个具体实例。1.关于MySQL中SQL操作和python脚本的结合MySQL作为一个关系型数据库它为我们日常的CRUD提供了有效的工具其中的各类型SQL语句自然是格外重要得益于python提供的跨平台能力我们得以进行基于脚本的自动化SQL这于我们之前提到的双端开发相对应。接下来我会基于一个简单的实例记录我作为一个新人的理解#!/usr/bin/env python3 ##shebang声明供python解释器进行识别 import mysql.connector from mysql.connector import Error ##导入库 DB_CONFIG { host: localhost, user: root, password: password, database: employees ##一些基本的MySQL配置可随着情况改变 } def main(): conn None try: conn mysql.connector.connect(**DB_CONFIG) if conn.is_connected(): ##作条件判断通过conn.is_connected()作为一个bool值提供判断依据防止无意义的消耗 cursor conn.cursor() ##游标定义 sql SELECT d.dept_name, COUNT(de.emp_no) AS employee_count FROM departments d JOIN dept_emp de ON d.dept_no de.dept_no WHERE de.to_date 9999-01-01 GROUP BY d.dept_name ORDER BY employee_count DESC; ##一些基本的查询语句可以随情况进行改变 cursor.execute(sql) ##执行 rows cursor.fetchall() ##使用 print(\emp_nums of departments) print(- * 40) for row in rows: print(fdepartment: {row[0]:20} emp_nums: {row[1]}) print(- * 40) except Error as e: print(ffailed:{e}) ##输出错误作为debug的依据 finally: if conn and conn.is_connected(): if cursor: cursor.close() conn.close() if __name__ __main__: ##通过检测这里进行对于main()的循环达到自动化 main()当然脚本本身的功能性可以进行不断的扩展比如通过加入crontab也就是Linux系统自带的一个定时功能实现定时执行其余还有很多会在之后的文章中随着我的学习实践陆续提及。2.关于RabbitMQ和跨平台通讯其实在选择RabbitMQ前我产生的主要疑问在于为什么不能使用Linux自己的消息队列我在之前的项目中曾经用到过Windows的消息队列而且由于Qt的使用经验我经常会和各种信号或者“消息”打交道最初的想法是将Linux自身的API进行调用从而获取MySQL的活动信号然后传回Windows同理Windows本身也会调用自身API进行和Linux的沟通——最开始我认为这只不过是以往经验的一次复用但很明显这是一个思维的误区。在Windows中我们有完善的hook和pipe技术并结合窗口消息队列实现单机内部的开发当然这也是我之前接触过的所以比较熟悉对于Linux这个我新接触的领域我只粗看了SystemV这种POSIX这种比较基本的机制但它们都只能在单机范围内发挥高效的功能回顾我们的实验环境Linux虚拟机和Windows宿主机在这时就需要一个合格的中间件才能实现跨平台通讯。对于正常的企业开发来说Kafka绝对会是个好的选择但是由于Kafka部署和运行操作带来的高成本和项目本身体量的限制百万级高并发的场景难以模拟于是更简单的RabbitMQ进入了我的视野接下来我会进行对于RabbitMQ的具体操作当然写这篇的时候我对它的了解还很有限我在这里会省略关于安装RabbitMQ的具体步骤但是有一点要提醒不要忘记下载Erlang环境RabbitMQ基于Erlang编写我们刚搭建的Linux虚拟环境是没有对应运行条件的。同样的如果你是Windows上的使用者对我来说使用RabbitMQ的首要问题就是如何将Linux和Windows两个不同平台的信息在Windows上汇集并进行管理基于当前环境我们假定Linux作为生产者Windows作为消费者其实我个人认为很多时候生产者和消费者的身份会发生转换这里先不谈我先演示一个基础的模型sudo rabbitmqctl add_user begoniazhao 0905 ##创建用户指定用户名begoniazhao和密码0905 sudo rabbitmqctl set_user_tags begoniazhao administrator ##设置指定用户的权限为管理员 sudo rabbitmqctl set_permissions -p / myuser .* .* .* ##具体的权限设置比如读写等等。现在我也就是begoniazhao用户已经被创建了不同于RabbitMQ自带的guest也就是默认用户guest只能在localhost上进行访问就好像是你自己创建一个本地8080端口的HTML本机确实可以流畅访问但是对于项目追求的对真实环境的模拟是远远不够的这也是RabbitMQ发挥用处的场景sudo ip addr show 1: lo: LOOPBACK,UP,LOWER_UP mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: ens33: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 00:0c:29:3f:1b:0b brd ff:ff:ff:ff:ff:ff inet 192.168.153.128/24 brd 192.168.153.255 scope global dynamic noprefixroute ens33 valid_lft 1405sec preferred_lft 1405sec inet6 fe80::20c:29ff:fe3f:1b0b/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: docker0: NO-CARRIER,BROADCAST,MULTICAST,UP mtu 1500 qdisc noqueue state DOWN group default link/ether d6:57:bf:f5:5d:65 brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever ##当前运行的就是Linux自带的IP show 指令可以看到 ##一串域名192.168.153.128那就是我们需要的Linux虚拟机IP下面我们要用到它下面结合具体的消费者consumer.py脚本为示例import pika ##导入需要的库 credentials pika.PlainCredentials(begoniazhao, 0905) ##指定用户和密码 connection pika.BlockingConnection( pika.ConnectionParameters(host192.168.153.128, credentialscredentials) ) ##指定对应的LinuxIP地址 channel connection.channel() ##声名链接通道 channel.queue_declare(queuehello) ##定义队列为hello def callback(ch, method, properties, body): print(f [x] Received {body}) ##发送信息至于body我之后和数据包转发中的header一起说 channel.basic_consume(queuehello, on_message_callbackcallback, auto_ackTrue) ##callback回调接触过Windows消息队列或者Android开发的JNI和API接口编写会很熟悉之后会展开说 print( [*] Waiting for messages. To exit press CTRLC) channel.start_consuming()当然这只是最简单的RabbitMQ随着后续的学习我会学到更复杂的部分那时我会对它产生更深入的理解并给出更好的记录。三.关于通讯在我的个人理解中项目基本的通讯应当以Windows作为中间件或者比喻成邮箱更加形象——Linux作为服务器serverWindows作为web网关前台Android作为native终端。其中Windows还会承担RabbitMQ的面板功能因为Windows上的Edge浏览器提供了一个本地HTTP管理面板运行的有效环境上面提到过的Linux IP在这里又可以发挥作用这里我们发现了一个陌生的端口15672我这里和5672一块讲解同时也在这里进行一个提醒以我使用的Ubuntu发行版为例如果开启了系统防火墙功能UFW那么就需要我们进行相关功能的设置具体来说可以理解为放行某些特定的端口sudo ufw allow 5672/tcp sudo ufw allow 15672/tcp sudo ufw status ##这里我们看到了TCP协议这也是RabbitMQ得以实现跨平台通讯的关键 ##和AMQP一样构成了RabbitMQ这类消息队列的基本逻辑 ##之后随着我学习的深入 ##我会给出专门的记录和个人理解对于5672端口其实就是RabbitMQ的默认端口类似于MySQL默认端口为3306负责链接消费者和生产者对我们的环境来说就是Windows和Linux让Windows上的消息得以正确的传到在Linux上运行的RabbitMQserver。而对于15672端口则是作为HTTP的管理端口我们之前提到过的guest和自定义的begoniazhao这里会体现出差别——guest作为default的默认用户不能访问15762它只能在Linux虚拟机上的localhost进行访问因此我们的begoniazhao作为自定义的管理用户在这里就可以发挥作用。在这里我们得以发现在项目的模拟环境中Linux和Windows已经构成了一个仿真的远程服务器和运维环境——Windows的begoniazhao这里就实现了对Linux上RabbitMQ的”远程访问“而Linux虚拟机成为了”服务器所在的内网“。后续的一些想法作为一个刚上大二的学生当前的项目体量其实有些偏大截至到今天我对项目的具体构造才开始大概一周不到。幸好课余时间目前还是比较充足。如果让我说个人的体会我会说深度不会太大但是广度确实是我遇到最大的从网络协议数据库运维再到不同系统的调试编程语言的多样性......总得来说符合预期实践起来也出现了一些挑战。作为一个学习者我认为这个项目至少可以锻炼我的工程能力和思考能力通过不断的学习和实践总归是有些用的。项目的完整实现估计会耗费我至少半个学期作为一个长期的专栏后续我会不断更新大概一两周一篇记录自己的一些技术总结和想法如果有人看到这里我欢迎你指正我的一些错误当然如果帮到了你一点点小忙我也会衷心的为你感到开心。