厉害一些的服务器设计(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部分的内容整理出来!

睡觉~