7年

2012年,我离开了生活了18年的杭州,来到了成都,来到了电子科大。

因为从小就对电脑充满了兴趣,所以我看了学校名字就选择了这所它。

就这样,呆了7年。

Career

记得那一年iPhone特别火,还看到了励志故事,说一个小朋友通过做iOS的应用挣了他的第一桶金。

从小就喜欢做游戏的我,也觉得做手机游戏很有前途,所以得到第一台电脑之后就开始学习iOS的编程。因为初中高中就学了pascal,暑假还看了一些VB,所以大一时候的C语言课对我来说,非常简单,刚好可以自学一些其他的东西。

那个年代还是个穷小子,iOS开发者账号的年费对我来说是一笔巨款,所以那时候还得将手机越狱才能真机调试。当时还是苹果的系统还是iOS5.0。即使不知道iOS App的工作原理,也可以跟着教程做出一些简简单单的小应用。对于一个一直在命令行里跑程序的小孩子来说,能看到界面,可以触摸交互,确实是很激动的一件事。

做了一些小demo之后,很巧合的加入了飞凡网络工作室,这是个专门做iOS应用的工作室,在这里我也做了人生第一个应用。知道了HTTP请求,知道了第三方框架,并更加深入的理解了interface(protocol),对面向对象也有了更好的理解。

大一上我还是个认真学习的好孩子,加入工作室后,我变学会了带着电脑去上课,哈哈。

大二的时候通信学院的互动体验中心(IEC)的iOS前辈要去实习工作了,于是全网征集iOS开发者。在飞凡当时的头头阿雀的推荐下,我进入了IEC。被方前辈手把手指导了一番后,我的iOS开发进入了下一个阶段。我知道了开源社区,知道了包管理,知道了如何快速快速的搭建一个app。

在IEC的第一年,一直在维护这Combee这个项目,可以说他就是早起的钉钉,每一条消息都会标记是否收到,而且提供了短信和电话的通知,是不是还真的挺像的~

大三的时候老陈让我参加了精益防伪的项目,做NFC听起来挺好玩的,但是,这迫使我去写Android的应用。与此同时在阿雀的介绍下,我被推荐给了黑盒子(晨电智能前身),去做炫轮的iOS App,这才开始接触蓝牙,从此开启了智能硬件之旅……同时这一年外卖行业突然兴起,一个餐馆接入了多个外卖平台接单特别麻烦特别累,而且用户根本不知道自己的外卖到哪儿了;所以RaPoSpectre带着我们做了Alinone,一个外卖整合平台,用我们的平台就可以把多个外卖平台的订单一起拉下来,配送员扫码后可以在手机上看到用户的手机号,地址,导航;同时位置信息也会上传,所以用户可以在我们做的查饭宝网页上看到自己外卖的具体位置。当时外卖平台们没有做这个这样一套系统,所以我们的系统还是收获了一些商家用户的~不过后来,就平台自己做了,我们当然就凉了~

一开始比较菜,就所有的东西都找开源库来做。慢慢的发现,开源库可以快速的实现功能,但是并不一定适合每个项目本身。开源库在设计的时候,没有考虑过项目的模块和各种设计模式,这会导致过分以来开源库会导致后期一些重构工作变得非常困难,所以慢慢的就开始总结并开始做自己的库。

在晨电智能的5年让我提升了很多,对软件进行设计,对产品进行设计,进入工厂对测试生产进行优化,慢慢的我对产品有了感情……对产品的每一个细节都变得苛刻了起来。由于人手不够,所以顺便好好学了学Android,随便学了学Web,还顺便搞了搞服务器。

研究生期间做了电力线通信的一个项目,其实还挺有趣的。我一直知道,嵌入式软件就是在大循环里面跑程序,但是一直没有机会实际操作一下。终于这个项目让我好好过了一把嵌入式的瘾,狠狠的用了keil,下程序,串口调试什么的真是太好玩了。

最后的半年,和ddlxx在406学习的那段日子,也感觉自己长进了许多。玩了很多新的花样,Docker,分布式,消息队列,redis,微服务,树莓派……

回过头一看,自己真的不再是一个只会做app的小朋友了~

Happiness

似乎,我对IT真的很有兴趣,所以,学这些东西,特别开心,并不会觉得枯燥和无聊。

做项目的时候会比较烦,有时候还会很恼火,甚至拍桌子咒骂,但是一旦出现阶段性成果的时候,那种感觉还是挺爽的。

就像第一次做一个带网络请求功能的iOS App,

就像精益防伪的测试系统第一次跑通,

就像炫轮App的传输速度一次一次的提升,

就像电力线三级路由通信成功,

做出他们的时候,就觉得,熬的那些夜,吃的那些苦,掉的那些头发,都是值得的~

Friends

7年过去了,但是大把大把的朋友还是留了下来。

除了可爱的室友们,

还有让我提升、带我入门的前辈

工作时互相帮助、互相理解、互相鼓励的同事

经常表面互吹,实际上互相分享、讨论前沿技术的兄弟

当然还有那些一起流过血、吃过鸡、拆过炸弹、救过人质的战友

You

每一件事情的都不会无缘无故的发生,一定有一件或者多件与他有关联的因素,导致了事件的发生。

而你,是这一切的开始……

缘,妙不可言。要不是你高考考差了,我也考差了,说不定我们就不会相见。

没有你的推荐,我也不知道飞凡工作室,也进不了IEC,去不了晨电智能,或许也不会在国内读研,也就不会有今天这一篇博客。

按照最初的计划,我可能会一直好好学习,然后去国外读研究生吧。但是你帮我打开了我的职业生涯……

Spidey_moi,让我看到了什么是智商压制,让我明白了地震不可怕,让我接触了硬件和嵌入式,让我喜欢上了小蜘蛛,让我爱上了川菜,给了我动力,给了我鼓励,给了我一切我需要的。

Good Bye

如果这个夏天再长一点,我们是不是可以再好好说一声再见

我们说了再见,后来,是不是就真的可以再见

再见了IEC

再见了晨电智能的兄弟姐妹

再见了406的孩子们

再见UESTC

成都,再见。

小米生态链

马上要离校了,图书馆借的书还是要赶紧看看完。

这本小米生态链的书,是我的头头推荐给我的。一直看到断断续续的,现在眼看着deadline要来了,就集中精力把它看完了。这,可以当作一本创业的书,小米,以及它衍生的一系列产品(也就是小米生态链中的产品),都算是一个创业公司,

此文流产,有空再改

成长的烦恼

Big Data && Machine Learning

这两年人工智能的发展突飞猛进。人工智能用来预测用户习惯、预测会发生的事情非常管用。

其实人工智能的基础的基础的基础的基础,就是概率。通过海量的海量的海量的数据,将多种结果进行分类,训练得出模型,然后便可以推广到一般情况去。

其实这就是经验。人类本身就是智慧动物,其中一个原因就是,会举一反三,会根据经验去提升自己的行为。

对于很科学、很理性的事情来说,举一反三是个很好用的能力。因为这些事情是有逻辑的,可以通过推理推倒得到预测的结果。

对于感性、没有道理的事情来说,预测是很难的一件事。靠着自身经历,身边朋友的经历,去预测别人未来的生活,是不合理的。因为一个人的经历以及他知道的身边朋友的经历的数据样本数量非常小,即使一个人很厉害,社交能力很强,知道了某一类事情的100个不同朋友的经历,也无法推测出另一个人的结局。因为样本太少了,变量太多了。人和人的差异数量,很难量化;假设总结了50个描述人的特性,或许影响事情结局的恰好就是没有总结到的第51个特性。

所以,经历只能参考不能准确的预测。

可能某些人会说,历史就是用来借鉴的。不过仔细观察,历史是一条连续的没有停的时间线,从古至今,历史这条线一直在持续的书写下去。但是人生是不一样的,每个人的一生是一条条平行独立的时间线。

那么借鉴历史的过程其实是:观察历史这条线,若干年以前发生的事情,不能重蹈覆辙,得避开,因为一旦重蹈覆辙又会沿着之前的时间线发生下去。

但是借鉴个人或他人的经历就完全不同了:观察别人一生中的一段成功经历,把他做的事情拿来重新做一遍,就会成功嘛?观察他的一段失败经历,避开他做的一些事情,就一定不会失败嘛?

并不会,因为时间在变,时代在变。所以,别预测!

ddlxx给我了几个正面的例子,至少也反向说明了,预测是不准的!

Prejudice

偏见,真的是个很头疼的东西。

和上一章对应起来,我们发现网络暴力中又一个现象就是:地域攻击。这其实就是一种偏见。比如你身边的几个东北兄弟,或者影视剧中的东北兄弟很能喝酒,你就觉得所有东北的兄弟都很能喝?(@yhdbzt)

当你对一个(类)人产生偏见之后,他就被套上了一个滤镜,无论他做什么事情,都会被错误的理解。

比如韩剧里面的灰姑娘无论做多么善良多么单纯的事情,都会被豪门的家庭认为是:心机

他们会把所以邪恶丑陋的想法强加在女主身上。

而且,这种偏见很难消除,他会一直保留,直到有一天,女主变成了白天鹅。

韩剧当然很美好,每个灰姑娘都可以变成白天鹅。但是现实还是很残酷的,灰姑娘不一定能变成白天鹅,但是她还是可以变成灰天鹅的呀!

那好歹也是天鹅,难道因为偏见就把她一辈子当作丑小鸭看待嘛??

Regret

我很怕,被迫做出的选择会让我后悔一生。

沈师兄说:自己做的决定一般都不会后悔。师姐说:被迫做的决定,才会后悔。因为你会埋怨给你提建议的人。

或许从小到大我过的太顺利了,没有遇到人生挫折的时候,不会去抱怨人生,也就不会后悔曾经做过的决定。

写到这里的时候,我突然发现我其实有后悔做过一件事~就是本科选专业的时候应该选物联网工程,这样说不定我就保研了,哈哈

不过很难说我去了物联网工程会不会也会后悔,比如保研了读了学硕,然后被论文折磨死。比如读了物联网工程就不会有这么扎实的局域网、接入网等网络知识的基础。(这些知识对我的帮助真的很大很大),所以有时候我觉得自己没选错,一切都是缘。

所以自己做的选择,真的很难后悔。被迫做的选择,真的可能后悔。

Growing Trouble

这段时间我过的还挺难受的,好朋友们都来开导我安慰我。

Jerry说:你长大后,会发现曾经遇到的问题和困难,都不是问题。

小学时候,没戴红领巾就觉得自己完蛋了。

初中时候,假期结束前一天晚上发现作业还没做完就觉得明天要被老师锤死了。

高中时候,高考发现该做出来的题目竟然没做对,就觉得人生完了。

本科时候,毕业论文写不出来,觉得自己肯定毕不了业。

硕士时候,项目做不出来,交不了差了,觉得以后混不下去了。

…………

这样的例子可能还有很多。可,回过头去看,他们都不是问题。在那一刻,可能觉得无路可走,被逼上绝路了,但其实说不定都是小事~

所以,真的很感谢大家,让我想明白了很多事情。

Old Enough

这么多年一直做一个听话的孩子,对于一些事情,我愿意妥协。

但是,现在我已经很大了,我没有提出什么意见,并不代表我没有主意。

我并不是不听话的人,所有人给我提的意见,我都会听到心里,但是不会直接改变我的想法,因为我会思考了。

大家给的建议和意见都带着说服人的目的,所以一定会避重就轻,回避自己游说体系中的弱点,以达到洗脑的目的。所以面对面聊天时,很难找到他们的破绽,但是事后慢慢思考,还是可以发现其中的漏洞的。

Freedom

我们这一代的年轻人,应该是更追求自由的一代人。由于父母提供了较为不错的生活条件,这使得我们有更多的机会,更多的可能去做自己想做的事情。

我们想学什么就去努力学什么,想做什么就去认真的做。我相信任何一个领域,都是有可能做出一番事业的。

我们应该自己考虑自己的未来,毕竟人生只有一次,首先还是要为自己而活。

被父母一路安排的人生,是他们的人生,并不是我们自己的。

不如一路向西去大理

为了节省流量,这里放的是很小的缩略图

但是点击小图,可以看原图

毕业之前去云南度假了一下下,一直想去看看洱海,寻找那种平静的感觉。

所以住在了洱海边,看着它,吹着风,听着音乐,发着呆。

仿佛时间真的停止了下来,整个人都和这眼前的景一样,静止了下来。

等以后有了更多的时间和钱,一定要来多度假几天!

微服务架构练习-2

Context

上回书我们开启了一个微服务的练习项目,并写了一个基于gRPC通信的Account Service和网关服务。这一回主要来讲讲我们做性能测试时候的故事~

dedeluoxixiexpress写了两个路由,/grpc/,/native/,在gprc路由下直接调用Account Service,这个Service会通过PeeWee去访问PostgreSQL数据库服务器。native路由下,直接用node.js通过node-postgres去访问PostgreSQL数据库服务器。

Test 1

Express牛逼的地方就在于它擅长处理高并发,而且做转发比nginx方便多了。所以我们就看看他的本事把。

第一项测试是比较用grpc和直接native请求的性能。

test1

用jmeter进行压力测试,先进行并发量1000的测试,结果发现native出现了很多数据库连接问题,大概是node-PostgreSQL的库做的不够好,没有做连接池和线程的优化,导致了数据库接收到了express过多的连接请求,就导致了连接失败产生错误,最后整个网关服务都死了。但是看grpc的测试,就可以发现网关一直没有报错,但是收到数据request请求之后产生response所耗费的时间会随着并发量的增加而延长。当jmeter的测试中设置了超时时间时,会出现较多的错误,但是express的网关服务还是很坚挺的。

Test 2

经过第一个测试,我们决定排除native的方法,我们认定用了微服务的方式,至少可以保证网关服务不会出现拒绝服务的情况,仅仅保持http的连接对于express来说还是可以搞定的。那么现在第二个测试主要就是测试使用了微服务后,它的并发量极限是多少。

在这之前需要对测试工具JMeter做一些了解。

JMeter

JMeter是怎么进行并发测试的呢?

在开始测试后,软件会开始建立线程,需要测试多少并发,就会建立多少线程,在建立完线程后,在每个线程中开始进行测试。所以经过观察我发现,它并不能很好的模拟一个高并发的测试,因为多线程其实 从原理上来说还是时间分片做的,所以每台电脑做多同时发送的网络请求数目就那么多,并不能模拟我想要的数量。

JMeter也知道这个问题,所以它提供了分布式测试的功能,说起来就是,自制DDos攻击,哈哈。

所以我们就发动了教研室里面的6台电脑,一起测试,每台就1000左右的并发测试,果然发现测试结果比用一台电脑惊醒6000并发测结果要糟糕的多。可能因为使用的全是超低配服务器,能做到这个性能已经不错了吧。

虽然似乎访问都有了结果,但是从数据库里面的时间变化量可知,存在数据库写入失败的情况。

Test 3

我怀疑,是不是数据库服务器实在是太垃圾了,所以我就买了一台比较好的服务器来做数据库服务器。

test3

为了测试微服务的横向扩展是否实现,特地又把那些便宜的服务器拿来当作微服务服务器来测试。这里在网关服务使用一个假负载均衡机制(随机调用)来进行测试。

从3个account service的log中可见,三个微服务都被临幸了。最后看7000左右的并发效果还是不错的,大概有10条以内会出问题,而且问题可能出在发送机这里。他们发不出去了!

Router Limitation

电脑不能同时发那么多数据,难道我们用的路由器就可以了嘛?

我们提高每台电脑的并发参数后,开始测试时,发现网内的其他电脑的网络速度变得很差了。甚至连ssh终端的连接都无法维持。所以这个测试可能就被限制住了……

Test 4

对于并发测试,我觉得已经很满意了,因为在这2天左右的时间里把grpc搞了搞,docker用得更熟了,还买了很多服务器…很多低端服务器。用这些低端服务器维持了这么高的并发,我觉得还是可以的了。所以接下来,在微服务中很重要的就是服务的发现和负载均衡。

在查询相关资料的时候我发现,nginx已经和gRPC融合了了。他官网还给出了例子,好好讲解了怎么样用nginx来配合使用gRPC。

幸好还剩一台服务器,所以设计了一套新的测试方案。

test4

用nginx自己来做负载均衡,说不定很科学。

但是测试的结果并不理想,比Test 3的效果还要差,至于这个原因嘛,下回分解把。

微服务架构练习-1

Context

马上要毕业了,在毕业后不知道还有没有机会做一个自己想做的东西了,所以想把很早之前看的和微服务有关的系统架构实现一遍。为了实现一个这样的系统,需要先设计一个有高性能系统架构需求的系统,不然永远停留在理论阶段,根据实际的情况做,才会讨论出更多的灵感。

先甭管这样一个系统有没有用,先把功能简单说一下。这是一个广告分发系统,用户可以对某一台或多台广告显示装置上显示的内容进行购买和编辑。服务端接口分为http服务和tcp服务两部分,http服务用来处理用户的请求,tcp服务用来处理嵌入式广告显示装置。
这样的设计在之前的防盗定位系统和轨迹追踪系统中都已经实现过,方案是可行的。之前把所有的http功能都做在了一个django服务器中,将tcp的功能用twisted实现,并通过RabbitMQ将django和twisted进行连接。

这次为了引入微服务架构,那肯定要将各种功能拆分开。比如用户服务,订单服务,商城服务,设备服务和网关服务等。

Docker

微服务的一个好处就是,可以轻松的横向扩展,比如用户服务的业务需求量远远大于订单服务的业务需求量,那么可以多增加几台处理用户服务的服务器,分散处理的压力,提高系统中该业务的并发量。

在使用docker之前,布置一个服务器都是手动ssh登陆,手动安装环境,用git把代码pull上去,然后启动。

配置环境有的时候还挺复杂,比如安装nginx啊,uwsgi,django,数据库啊.如果在一个系统中需要运行多个服务的时候,需要打开端口,比如uwsgi要开9000,postgres要开5432,rabbitmq要开5672等等。这样会导致服务之间存在不安全性,比如uwsgi服务本不该访问5432端口,但是当系统被攻击时,说不定黑客可以通过一个服务的漏洞访问到其他的服务。

使用docker可以为每一个服务建立一个容器,容器之间有特定的通信通道比如,nginx容器只能访问python(django+uwsgi)容器的端口,python容器和postgres容器有特定通道,python容器和rabbitmq容器有特定通道。这样postgres容器和rabbitmq容器之间就完全隔离无法通信了,达到了安全隔离的效果。

使用了docker之后,可以轻松方便的将一个镜像复制很多份。这对于部署一个分布式系统来说真的是轻松了不少,横向扩展一个微服务也就只要使用docker run多个镜像就好了。

Docker确实够方便了,但是我还嫌麻烦,因为要一台一台一台的输入docker run xxx这样的命令,当有服务要更新了,也需要同样的将对应服务的docker镜像重新更新后再启动。所以就有了docker的三剑客,compose和swarm

Docker Compose

docker compose是将一系列的docker配置信息写在一个文件里面,运行的时候,可以将全部的服务一次性运行,也可以单独的重启任何一个服务。

而且使用了networks的alias配置后,容器之间的访问甚至可以用hostname直接访问,都不需要配置ip地址就行了,实在是方便。

因为我懒得找commit history了,所以就把比较新的一个docker-compose文件贴一下。

version: "3.7"

services:
  db:
    build:
      context: ./AccountMicroService/
      dockerfile: Dockerfile-DB
    container_name: ms_practice_postgres
    volumes:
      - type: volume
        source: database
        target: /var/lib/postgresql/data
    networks:
      inner:
        aliases:
          - db
  account:
    build:
      context: ./AccountMicroService/
      dockerfile: Dockerfile
    container_name: ms_practice_account_service
    depends_on:
      - db
    ports:
      - "50051:50051"
    command:
      - /bin/sh
      - -c
      - |
          python /code/AccountRPCServer.py
    volumes:
      - type: bind
        source: ./AccountMicroService/
        target: /code/
    networks:
      inner:
        aliases:
          - account
      outter:
        aliases:
          - account
  tester:
    build:
      context: ./TestService/AccountTest/
      dockerfile: Dockerfile
    container_name: ms_practice_account_tester
    depends_on:
      - account
    volumes:
      - type: bind
        source: ./TestService/AccountTest/
        target: /code/
    command: python /code/account_rpc_test.py
    networks:
      outter:
        aliases:
          - tester
networks:
  inner:
  outter:
volumes:
  database:

这个compose文件是测account这个服务的。其中有三个服务数据库服务、用户服务、测试服务。定义了两个网络,内网和外网。

这个服务我使用了gRPC来进行调用,gRPC的相关内容会在后面讲。

在这里,我公开了account服务的50051端口用于gRPC的调用。在account和db之间建立了私有通道,以便进行数据库的增删改查操作。

Docker Swarm

Swarm的用处就是帮助部署集群,但是我觉得挺难用的,因为只能使用image部署,不可以通过编译dockerfile来部署,所以对于有文件洁癖的我来说,还是有点儿不爽的,可能以后学了k8s或者其他工具之后,会有更方便的集群部署方式。

Communication

根据微服务的基本设计,系统中需要有服务发现和服务注册的功能,微服务之间需要互相通信,这次使用gRPC来做这样一个通信的工具。因为很方便,只要确定了通信消息,就可以在server和client之间进行通信,比rabbitmq还方便(当然因此他也存在一些劣势)

grpc工作示意图

这里简单设计了一下用户服务的通信原型Service.proto:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.ms.account";
option java_outer_classname = "AccountProto";
option objc_class_prefix = "ACP";

package accountservice;

service AccountService {

    rpc SignupNewUser (SignupMessage) returns (SignupResponse) {
    }

    rpc LoginUser (LoginMessage) returns (LoginResponse) {
    }

}

message LoginMessage {

    string username = 1;

    string password = 2;
}

message SignupMessage {

    string username = 1;

    string password = 2;

}

message LoginResponse {

    int32 status = 1;

    string message = 2;

    string userid = 3;

}

message SignupResponse {

    int32 status = 1;

    string message = 2;

    string userid = 3;
}

这里为了测试,只定义了两个服务调用,就是注册和登陆两个服务。

用python写了gRPC Server:

import service_pb2
import service_pb2_grpc
import AccountDB
import grpc
from concurrent import futures
import time

_ONE_DAY_IN_SECONDS = 60 * 60 * 24


class AccountServiceReceiver(service_pb2_grpc.AccountServiceServicer):
    def SignupNewUser(self, request, context):
        print("receive sign up request")
        if len(request.password) < 6:
            msg = "password not strong"
            return service_pb2.SignupResponse(status=-2, message=msg, userid="")
        else:
            user = AccountDB.signup_user(request.username, request.password)
            if user is not None:
                msg = "succeed"
                return service_pb2.SignupResponse(status=0, message=msg, userid=str(user.userid))
            else:
                msg = "username existed"
                return service_pb2.SignupResponse(status=-1, message=msg, userid="")

    def LoginUser(self, request, context):
        print("receive login request")
        userid = AccountDB.login_user(request.username, request.password)
        if userid is not None:
            msg = "login succeed"
            return service_pb2.LoginResponse(status=0, message=msg, userid=str(userid))
        else:
            msg = "login fail"
            return service_pb2.LoginResponse(status=-1, message=msg, userid=str(userid))


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    service_pb2_grpc.add_AccountServiceServicer_to_server(AccountServiceReceiver(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(_ONE_DAY_IN_SECONDS)
    except KeyboardInterrupt:
        server.stop(0)


if __name__ == '__main__':
    serve()

gRPC很厉害,他是非阻塞的,所以按照官方的教程来写的话,这个server的代码运行一下就结束了,所以需要增加一个人工阻塞使得代码可以一直运行着。

就是这样的:

try:
    while True:
        time.sleep(_ONE_DAY_IN_SECONDS)
except KeyboardInterrupt:
    server.stop(0)

这里的service_pb2和service_pb2_grpc是gRPC官方提供的工具,他可以根据proto文件生成对应的python文件,以供调用。AccountDB.py是一个数据库操作代码。

在server完成后,可以设计一个client代码来测试grpc的功能。

account_rpc_test.py:

import grpc
import service_pb2
import service_pb2_grpc
import uuid


def signup(stub, username, password):
    signup_message = service_pb2.SignupMessage(username=username, password=password)
    signup_result = stub.SignupNewUser(signup_message)
    return signup_result


def login(stub, username, password):
    login_message = service_pb2.LoginMessage(username=username, password=password)
    login_result = stub.LoginUser(login_message)
    return login_result


def run_test():
    with grpc.insecure_channel('account:50051') as channel:
        stub = service_pb2_grpc.AccountServiceStub(channel)
        username = str(uuid.uuid4())
        password = str(uuid.uuid4())
        signup_response = signup(stub, username, password)
        print signup_response.message
        signup_fail_response = signup(stub, username, str(uuid.uuid4()))
        print signup_fail_response.message
        login_response = login(stub, username, password)
        print login_response.message
        ogin_fail_response = login(stub, username, str(uuid.uuid4()))
        print ogin_fail_response.message


if __name__ == '__main__':
    run_test()

由此便可以测试grpc的功能来

网关服务

上面仅仅是完成了微服务和数据库的连接,以及相应的操作。对于http client来说,现在并不能直接调用。为了测试http的并发量,所以得先增加一个http网关。

dedeluoxixi最近一直在吹爆node.js,所以就由他来做了

index.js:

const http=require('http');
const express=require('express');
const logger=require('morgan');
const app=express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));



app.use('/native',require('./router/native'));
app.use('/grpc',require('./router/grpc'));
app.get('/test',(req,res,next)=>{
    res.json({msg:0})
});

const server=http.createServer(app);
server.listen(3000,()=>{
    console.log('Nodejs running on port 3000');
});

不得不说,用node来做一个简简单单的http server是真的简单啊。

我们使用了两种方法来做对比:

1.直接使用node的http server来操控db

2.使用node的http server的grpc来调用account service再来操控db

至于结果如何,且听下回分解,哈哈哈

咻咻咻咻

Context

之前一时兴起买了raspberry pi,本来想用来加上摄像头来做监视器的,但是时间一长就忘记了~

撸完论文就想着,要不要把它用起来,这样就不会感到内疚了。

灵机一动,想到了支付宝以前的声波支付,觉得很有意思,我也拿来做一下吧。

几年前支付宝刚推出声波支付的时候,我们恰好学习了信号与系统,于是我就用手机采集声音,然后进行频谱分析,印象中在20kHz左右的位置出现了两座峰,用Matlab进行过滤掉高频信号后进行播放依然是咻咻咻咻的声音,所以很明显数据是通过高频信号传输的。

这次,我打算也做一套这样的系统,用raspberry Pi做下位机,用手机发出声波数据。就和支付宝声波支付一样。

这个系统想想,好像还挺简单的,但是越做坑儿越多~

下面几章顺序可能有点儿乱,因为我一边做一边改方案,所以做完之后已经忘了学习顺序了,哈哈。

Sound

这次做这个系统,特地借了几本声学的书。

学到了几个新的名词,有声音遮蔽,有声音痛感等。

声音遮蔽还挺有意思的,是说对于人的耳朵来说,某个频率的声音响度高于一个阈值之后,会将其他频率的声音遮蔽。用这个方法可以做数字水印,信息隐藏。

在一条不安全的语音线路上,用语音信号遮蔽隐藏的数据信号,可以迷惑劫持线路的中间人。这还是挺有意思的~

Signal && System

在学习信号与系统这门课之前,都不知道信号还可以在频遇上进行分析。一些时域上很复杂的信号,通过傅立叶变换后进行频域分析,竟然可以得到不复杂的频谱图。

频段串扰?

一开始我打算用两个频段混合使用,这样一个时间窗口内一个频段可以代表一个0或1,如此一来可以一个时间窗口就可以用两个频段来传2个码元了。

但是传输时,发现只能看到一个频段,另一个频段基本看不到,而已我选用的17.5kHz和19.5kHz两个频段进行传输,这两个频段的声音人耳一般是听不见的(反正我是听不见),但是两个使用两个频段一起传输时,可以听到很奇怪的噪声。

我很怀疑是不是不能一个时刻传两个频段的声音,于是去翻了信号与系统的课本。虽然说课本上写着

x(t)+y(t)->X(w)+Y(w)

不过这个可是周期信号,在某个时间窗中两个信号并不是周期信号,所以可能并不能直接加起来,也就是说,会出现新的频段。也就是噪声频段。

这大概是DSP领域的知识了,学得不好下次再研究~

奈奎斯特采样定律

学霸们的自信总是会出问题的~

再记一遍。采样频率是信号最大频率的2倍,一开始把奈奎斯特采样定律记反了,就挺尴尬的了。

系统使用44100Hz的采样频率。根据奈奎斯特采样定律,可以将最大频率为22050Hz的信号无失真的采集,这也是为什么数据传输使用17500hz和19500hz。加上信号带宽后,19500hz数据的最高频率应该还是小于22000Hz的。
所以从理论上来说,使用44100Hz是可以采集到数据的。

PCM

人耳听到的是模拟信号,但是机器只能处理数字信号,所以需要一个方法来将模拟信号转换为数字信号。这就有了PCM,脉冲编码调制。

在采样后,将每个采样点得到的电平分成x分,用一个整数来表示。比如8位的PCM,就将电平分为正负128位。16位PCM就将电平分为正负32768位。

位数越高,声音就采集的越准确。这也是踩了坑才想明白的~

一开始想节省一些内存空间,打算使用8位的PCM来进行发声,然后发出的声音其难听无比,而且完全没有把预想的频段信息播放出来,我甚至觉得是Android端的发声代码写错了。

后来我将PCM数据打印出来一看,确实连我自己都不能相信我发送的是正弦波。毕竟在高频率采样的情况下,几个点就需要表示数据信号的一个周期,如果使用8位PCM,很可能会将数据变成一个三角波的样子。于是我改用了16位PCM,就正常了很多。

Frame

帧格式还是要有的,毕竟是个数字系统。

帧头+数据长度+数据+Checksum+帧尾

其实还挺长的,确定了帧格式就可以设计状态机,从而做到一边接收一边解析。

ASK && FSK

一开始我还在纠结,我做的究竟是个数字通信系统还是模拟通信系统。因为我想了想感觉自己仿佛是在用17k,19k的载波进行调制后通信的。但是后面一想,我都有帧格式了,我都有码元了,那一定是数字通信系统了!

于是翻开通信原理这本书,看到了ASK、FSK、PSK等等等等,最简单的就是ASK了。使用一个频段进行通信,没有检测到信号则代表0,检测到信号则代表1,就是这么简单。

FSK也还行,一个频段代表0,另一个频段代表1。

通信系统和我的系统有些区别,就是通信系统的时间同步做的比较好,但是我的系统没有时间概念,容易出现数据黏包,而这个问题将在下一篇中好好讲讲

Decode

这个也留到下一篇去说,因为感觉挺长的~

End

这个系统做完后,我发现我又重学了信号与系统,通信原理,还学习了很多声学的知识。收获还是挺大的呢。

学了Android的音频开发,Python的音频开发等等,感觉这并不是个简单的系统哇。

2018 Stronger

Year 9

每年写年记,都可以看看过去的自己,看看自己变化了多少,自己的认知改变了多少,自己成长了多少,其实还挺有趣的。

翻了翻以前的年记,发现每年都说,自己变强了,感觉都不好意思了。但是万物总是在发展的,不变强就会被淘汰。

2018年可以分为两个部分,前半年在体会上班的生活,每天埋头苦干,晚上回寝室娱乐娱乐。后半年在埋头苦干的同时,开始思考未来。

还有6个月就要离开这个呆了7年的学校了,再也不可以自称为学生了,不能再享受学生优化,不可以必胜客8折,没有廉价的寝室,便宜的食堂,应有尽有的图书馆……

学生生涯,正式,结束了。

Work Life

2018的前半年,认真的工作。白天教研室,公司两头跑,完成了想都不敢想的电力线通信项目,完成了公司各种新产品的工具开发及软件开发。晚上看心情加班,回到寝室后会来上几把PUBG消遣消遣。

这样的生活,就是普通的工作生活吧。

做该做的事情,做完就休息。每天就这么过:起床,吃饭,工作,吃饭,工作,吃饭,回家,休息……看起来是多么的平淡啊。可是生活就是这样啊,不会到处都充满了惊喜,Tomorrow is another day.

Another working day.

或许,不应该把生活简写成这个样子,或许每件事情都充满了惊喜呢?看看一年来的新post,这些奇奇怪怪的项目,这些奇奇怪怪的坑,都是生活中的惊喜啊。今天的KFC早餐里面加了过去没有的泡菜,这也是惊喜啊。
今天在游戏中发挥出色,带队友吃鸡,这也是惊喜啊。惊喜并不是不存在,只是会被忽略。这正如工程师这个职业特点一样,在进行研发的时候,觉得平淡无奇,觉得生活失去了意义,觉得工作太累太无聊,可是一旦项目完成,
那便是巨大的喜悦,这种喜悦会让人忘却之前的烦恼,并自豪的说自己是个工程师。

所以每天的生活,应该这样记录:

睡了x小时起床、

吃了xxx早餐,路上扶起一位老奶奶、

工作,发现了好几个新的坑,学习了好多新东西啊、

在xxx吃了午餐,真难吃、

工作,喝这个牌子的咖啡效率真高、

回家做个xxx来吃

……

或许这样一看,每天都变得不一样了。可能这样一来,未来几十年的工作生活还挺有意思的呢。

Future

下半年,进入了研三,学生生涯的最后一年。同学们都开始忙碌了起来,找工作的找工作,实习的实习,申请博士的申请博士。

而我,也将面临一个重大的抉择。

3年前,准备读研究生,一开始的初衷,是为了创业,为了那个我看着长大的公司。3年过去了,当初那个稚嫩的创业公司,已经变得成熟,有了自己的造血能力,我一直以它为荣。

是去还是留,这是一个抉择。充满感情的做一件事情,会把这件事情做的很好,但是也会增加不舍感。一直以来我把每个产品的软件端,都当做自己的孩子来养,突然要和孩子说再见,确实会舍不得。

但是,对于我自己来说,这5年我得到了很多工程技术经验,但是错过了最前沿的技术。这几年学习新的东西,攻克新的难题,全是由我一人来完成,没有伙伴讨论,没有前辈指点,这限制了我获取新事物的渠道,
逐渐变得闭塞。这并不是一个好现象,假设为了若干年,依然是我一个人来负责公司的软件,我会不会变成一个避难从简的人,会不会变成一个还在使用2018年技术的过时人,会不会瞎指挥团队,会不会变成一个自己都讨厌的人呢?

或许,是时候去外面的世界看一看了。

Stronger

凭着几年的工作经验,顺利的收获了大厂的offer。但是,这让我陷入了恐惧,我开始担心自己的能力不足,开始担心自己的学识太浅。便进入了疯狂学习的状态。

我害怕我知道的太少,会的太少,未来被淘汰。于是,我选择跳出舒适圈,进入我更加不擅长的领域:架构。

假期回家,父母看到我认真看书的样子还挺惊讶的。他们觉得我在装样子,但我自己知道,我需要充电。一些知识和技术,在项目和工程中,是不会遇到的。因为项目和工程在需求分析阶段,
就会使用最趁手的技术去解决它,这就错失了接触最新技术的机会。而通过看书,确实可以弥补这个漏洞。

所以在2018年的后半年,通过图书馆的帮助,我看了很多最新的技术,并尝试了最新的技术。

一个工程师不仅需要经验丰富,而且需要与时俱进。

End

2019,有很多要面对的事情。

而事情,总是可以被解决的。

厉害一些的服务器设计(2)

context

继续昨天的内容,今天把twisted,pika整理一下

Let’s do it

twisted用来处理socket,pika用来和rabbitMQ做交互

twisted

twisted的官方文档介绍了很多用法,一开始我还以为twisted是个网络框架,在踩了一些坑,成功集成了pika之后,才发现自己错了。它更像一个runloop(loop)

在loop中注册事件,注册循环,来完成异步操作,循环操作等。和嵌入式的main()里面的那个while(1)很像,我就称他为:大循环

from twisted.internet import reactor

# 大循环就这么运行
reactor.run()

在大循环里面做点儿其他的事情:

from twisted.internet import reactor,task

# 用center实例来监听端口号为8123的TCP,center的具体实现后面再说~
reactor.listenTCP(8123, center)

# 在大循环中每0.01秒执行一次handle_message(),参数是queue_object
l = task.LoopingCall(handle_message, queue_object)
l.start(0.01)

reactor.run()

那么言归正传,先用twisted来搭建一个tcp服务器,先直接贴个代码:

socketEntity.py:

import datetime
from twisted.internet.protocol import connectionDone
from twisted.protocols.basic import LineReceiver


class SocketEntity(LineReceiver):

    def __init__(self, device_list, addr):
        #全局socket列表的 引用
        self.global_socket_list_reference = device_list
        #更新时间
        self.updateTime = datetime.datetime.now()
        #地址
        self.address = addr.host

    #收到了数据
    def dataReceived(self, data):
        print data

    #连接建立
    def connectionMade(self):
        print "new connection from {}".format(self.address)
        self.sending_connection_status(True)
        if self.address in self.global_socket_list_reference.keys():
            pass
        else:
            self.global_socket_list_reference[self.address] = self

    #连接丢失
    def connectionLost(self, reason=connectionDone):
        print "connection {} lost".format(self.address)
        self.sending_connection_status(False)
        if self.address in self.global_socket_list_reference.keys():
            del self.global_socket_list_reference[self.address]

    #更新远程数据库状态
    def sending_connection_status(self, online):
        pass

socketServer.py:

import json
from twisted.internet.protocol import Factory
from socketEntity import SocketEntity
from twisted.internet import reactor, protocol
from twisted.internet import defer
from twisted.internet import task


class DeviceCenter(Factory):

    def __init__(self):
        这个就是全局socket列表
        self.devices = {}

    def send_cmd(self, addr, cmd):
        if addr in self.devices:
            self.devices[addr].sendLine(cmd)
        else:
            print "no connection"

    # 这个是必须要的
    def buildProtocol(self, addr):
        return SocketEntity(self.devices, addr)


center = DeviceCenter()
try:
    #增加监听端口的事件
    reactor.listenTCP(8123, center)
    reactor.run()
except KeyboardInterrupt:
    print "socket stop manually"
except Exception as e:
    print "socket error:", e.message

我觉得,光靠注释应该差不多可以知道这个在干嘛。其实这个和twisted官方的tcp文档基本一样,如果发现跑不起来(因为删了pika相关的东西后,没有再调试)可以去官方文档重新搞一份

说说我的理解~

前面说到了twisted是个事件驱动的框架,所以会注册很多的事件,比如注册一个监听事件,注册一个延迟事件,注册一个循环事件~

注册监听事件reactor.listenTCP(8123, center),我觉得这个方法可能注册了很多小事件,这些小事件会调用DeviceCenter中的buildProtocol(),以及相应Protocol中的一些函数,比如dataReceived,connectionMade,connectionLost

pika

这个框架就是rabbitMQ client的python版本,通过这个库,可以轻松的发送message到rabbitMQ,也可以轻松的监听rabbitMQ

官方文档就写了很简单的用法

官方文档里面对连接认证这一块描述的还是挺简单的,但是通过pycharm的自动补全,或者查看源代码,就能知道如何设置连接的各种参数以及认证参数

官方sender.py

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()


channel.queue_declare(queue='hello')

channel.basic_publish(exchange='',
                      routing_key='hello',
                      body='Hello World!')
print(" [x] Sent 'Hello World!'")
connection.close()

官方receive.py

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()


channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)

channel.basic_consume(callback,
                      queue='hello',
                      no_ack=True)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

很明显,这里pika很牛逼,有他的start_consuming()会阻塞整个主线程,会一直监听queue,直到消费者出现异常,或者用户手动break。这个对于后面在数据库服务器做,根据rabbitMQ的消息来修改数据库中相应数据这个功能非常友好。基本来说,靠这个例子就行了。

twisted+pika

因此,pika的start_consuming()是万万不能用到twisted中的

好在,pika的官方文档刚刚好给出了twisted的消费者example

我也贴一遍:

# -*- coding:utf-8 -*-

import pika
from pika import exceptions
from pika.adapters import twisted_connection
from twisted.internet import defer, reactor, protocol,task


@defer.inlineCallbacks
def run(connection):

    channel = yield connection.channel()

    exchange = yield channel.exchange_declare(exchange='topic_link', exchange_type='topic')

    queue = yield channel.queue_declare(queue='hello', auto_delete=False, exclusive=False)

    yield channel.queue_bind(exchange='topic_link',queue='hello',routing_key='hello.world')

    yield channel.basic_qos(prefetch_count=1)

    queue_object, consumer_tag = yield channel.basic_consume(queue='hello',no_ack=False)

    #在主循环中增加一个read的任务,周期为0.01秒
    l = task.LoopingCall(read, queue_object)

    l.start(0.01)


@defer.inlineCallbacks
def read(queue_object):

    #消费一下
    ch,method,properties,body = yield queue_object.get()

    if body:
        print(body)

    yield ch.basic_ack(delivery_tag=method.delivery_tag)


parameters = pika.ConnectionParameters()
cc = protocol.ClientCreator(reactor, twisted_connection.TwistedProtocolConnection, parameters)
d = cc.connectTCP('hostname', 5672)
# 这个我还没有去了解是干嘛的
d.addCallback(lambda protocol: protocol.ready)
d.addCallback(run)
reactor.run()

虽然我最后写的和这个不太一样,但是踩了一下午的坑,才把它做对了~

下面贴一下我的server的代码。这个socket server的作用是,http server收到了http request之后,向rabbitMQ发送了一个消息,内容是向某个设备(address)发送一条命令(cmd),twisted tcp server收到了rabbitMQ中的消息后,作出send_cmd的操作

socketServer.py:

import json

from twisted.internet.protocol import Factory
from socketEntity import SocketEntity
from twisted.internet import reactor, protocol
from twisted.internet import defer
from twisted.internet import task
# 注意这里需要新建一个mqConfigure.py 并把相应的设置参数写在里面
from mqConfigure import *
import pika
from pika.adapters import twisted_connection


class DeviceCenter(Factory):

    def __init__(self):
        self.devices = {}
        存一个连接的引用保险保险
        self.connection = None

    def send_cmd(self, addr, cmd):
        if addr in self.devices:
            self.devices[addr].sendLine(cmd)
        else:
            print "no connection"

    def buildProtocol(self, addr):
        return SocketEntity(self.devices, addr)

    @defer.inlineCallbacks
    def handle_message(self, data):
        # 获取消息,并处理消息
        ch, method, properties, body = yield data.get()
        load_data = json.loads(body)
        addr = load_data.get("address")
        cmd = str(load_data.get("cmd"))
        self.send_cmd(addr, cmd)
        yield ch.basic_ack(delivery_tag=method.delivery_tag)

    @defer.inlineCallbacks
    def setup_mq_listener(self, conn):
        #这个时候rabbitMQ的tcp已经连接成功了,所以做后面的事情都没问题
        channel = yield conn.channel()
        yield channel.queue_bind(exchange='exchange', queue=MQ_QUEUE, routing_key=MQ_QUEUE)
        yield channel.basic_qos(prefetch_count=1)
        queue_object, consumer_tag = yield channel.basic_consume(queue=MQ_QUEUE, no_ack=False)
        #因为start_consuming()是阻塞的,所以就使用twisted里面常用的task手段来实现一个监听的操作(也挺像轮询的)
        l = task.LoopingCall(self.handle_message, queue_object)
        l.start(0.01)


center = DeviceCenter()

try:
    reactor.listenTCP(8123, center)
    # 连接rabbitMQ的参数
    parameters = pika.ConnectionParameters(host=MQ_HOST, port=MQ_PORT, virtual_host=MQ_VHOST,
                                           credentials=pika.PlainCredentials(MQ_USERNAME, MQ_PASSWORD))
    cc = protocol.ClientCreator(reactor, twisted_connection.TwistedProtocolConnection, parameters)
    # 我把它理解为异步操作的task对象,可以增加回调
    d = cc.connectTCP(MQ_HOST, MQ_PORT)
    #我依然不知道这个是干嘛的
    d.addCallback(lambda protocol: protocol.ready)
    # connectTCP成功之后的回调,设置监听MQ
    d.addCallback(center.setup_mq_listener)
    reactor.run()
except KeyboardInterrupt:
    print "socket stop manually"
    print "cleared data"
except Exception as e:
    print "socket error:", e.message
    print "cleared data"

那天花了比较多的时间浪费在理解twisted的运行机制上,所以让我感觉twisted非常难的样子。现在整理了一下,感觉其实并不难~

End

今天差不多就到这粒

明天或者后天,来写写数据库服务器如何处理rabbitMQ的内容,并修改Django的数据库;以及把整个系统做成分布式时需要考虑的一些细节吧~

厉害一些的服务器设计(1)

Context

这段时间看了一些架构师的书,感觉是时候练习一下了。

一开始订了一个小目标,做一个http+socket的服务器

后来想了想,把celery加进来

再想把redis也加进来

最后这个就变成了一个更厉害一点儿的系统了

http<->nginx<->uwsgi<->django->rabbitMQ,redis,celery<->twisted<->socket

由于最近还看了微服务的书,所以最后甚至想做一个分布式的服务器了!

Plan

这里不讲方案的演变过程,直接讲最后最后确定的方案。

一台高性能服务器装有 nginx,uwsgi,django,rabbitMQ,redis,twisted,celery

两台低配服务器有 twisted和celery

把这整个项目想像成,共享单车(非蓝牙开锁的)服务器架构~高性能服务器用来接受所有的http请求(手机app),所有服务器的twisted用来处理socket请求(共享单车)

其中celery用来做部分异步操作(假设有发短信,发送单车指令)的负载均衡

rabbitMQ用来做celery和twisted的Message Broker。celery的相关操作是自带的,不需要再写很多。而twisted集成rabbitMQ就需要用到pika,需要写一些东西才行。

redis用来做celery的task记录备份数据库,可有可无。

nginx,uwsgi,django不用介绍,就那些功能

最后还要使用JMeter来进行压力测试

Let’s do it

其实这个项目这样理一下,还是挺清晰的。

django就这么搭建,uwsgi和nginx就这么配置。

celery是最近重新好好看了文档之后又拾起来的东西,之前没有好好地用过它,只是知道它好像可以做异步操作而已。

celery

celery在介绍中就写了Broker,很明显他也是用消息队列来做进程间通信的。

celery对django有非常好的支持,具体的配置就要看这里

这里来一段假装是发送短信的异步操作,print这个地方改成一个向SMS网关发送请求的代码,就可以做到发送短信了。因为django的view里面的处理函数需要返回一个response,
但是django自带的发送网络请求的函数都是同步的,所以用了celery就可以很快速的返回一个response给客户端,在此同时再去发送短信,这就不会导致一个http请求产生多方等待的问题。

tasks.py中定义任务:

@shared_task
def send_sms(number, code):
    print "sending sms to {} with code {}".format(number, code)

view.py中调用:

@csrf_exempt
@require_POST
def send_sms(request):
    parameter = request.POST
    tasks.send_sms.delay(parameter["code"], random.randint(1000, 9999))
    return JsonResponse({"status": "succeed"})

配置的话,还挺重要的,我搞了一个celeryConfig.py,因为最后会涉及多个服务器一起部署,各自的参数会不一样,所以用一个外置文件来配置,很舒服(要把它gitignore了)

celeryConfig.py:

from __future__ import absolute_import

broker_url = 'amqp://***:***@10.139.***.***:5672/myvhost'
result_backend = 'redis://10.139.***.***:6379/0'

task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
enable_utc = True


result_expires = 60

这样配置rabbitMQ和redis的服务器路径就很方便了。

redis

上面说到的celery用到了10.139.***这样的内网ip,没错,现在多台服务器是共用一个redis的,所以需要配置redis让他可以对开放

网上很多都说去掉bind 127.0.0.1,关闭protect-mode就好,但是我觉得这个方法并不好,因为直接去掉这一行,会向全网开放,而我只想在内网开放,所以只要把

bind 127.0.0.1

改为

bind 10.139.*** 127.0.0.1

就好了。

但是这样运行起来说不定还是不行,可能会”could not connect”,因为linux还没有把这个端口对外开放。使用这个明令把6379端口给开了

/sbin/iptables -I INPUT -p tcp --dport 6379 -j ACCEPT

然后保存一下

/etc/rc.d/init.d/iptables save

这样应该就ok了

某天晚上想给别人装个逼,打开终端一看,redis跪了。之后发现redis只要运行一段时间就跪了,而且杀不掉,只能重启。之后回到寝室看了报错内容

Failed opening the RDB file dump.rdb (in server root dir /*****) for saving: Permission denied

其实问题很简单,只要把这句话里面的路径的文件的权限给了就行,就会自动恢复。

nginx uwsgi django

这三个东西可以说是用的很熟悉了的~

不过这次想要玩点儿新花样。nginx和uwsgi之间使用socket来通信,而不是直接端口映射

uwsgi.ini:

# mysite_uwsgi.ini file
[uwsgi]

# Django-related settings
# the base directory (full path)
chdir           = /***
# Django's wsgi file
wsgi-file          = httpServer/wsgi.py
# the virtualenv (full path)
home            = /***/venv

master          = true

processes       = 10
# the socket (use the full path to be safe
socket          = /***/advServer.sock

chmod-socket    = 664
uid = www-data
gid = www-data

vacuum          = true

workers=24
listen=65535

启动之后,nginx访问这个socket总是说permission denied,即便使用的sudo也不行。后来查到资料说,要他们有相同的user和group才行。但是我明明在ini中已经设置了和nginx相同的user和group,却还是报错。

于是最后我只能开大招了:

sudo chown www-data:www-data /***/advServer.sock

真的有效

rabbitMQ

先说说Mac安装了rabbitMQ之后会出现的一个很小很小的问题吧~

运行了./rabbitmq-server之后没反应,过了一会儿提示

error:epmd error for host ******: timeout

这个很简单,只要把这个*加入host就好了

修改/etc/hosts,增加:

127.0.0.1 *****

再试即可!

Pause

今天先整理到这里,明天再把twisted,pika部分的内容整理出来!

睡觉~