未分类

服务器性能优化初探

前言

本文将总结服务器性能优化的两种基本方法,反向代理和数据缓存,分别会使用到nginxredis,关于反向代理和数据缓存的基础知识暂时不做介绍,本文将以图文并茂的形式详细地记录安装配置nginxredis,以及使用它们来简单实现反向代理和数据缓存的过程。

1、Nginx 安装与 Proxy 的配置

nginx的安装过程如下,由于部分命令运行后的输出比较长(例如make),截图比较麻烦,下面我记录了安装过程中更需要用到的命令。首先切换到root用户方便后续操作。接下来开始安装pcre库,安装的命令如下所示,安装完成后通过检查版本来判断是否安装成功。然后才是开始安装nginxnginx的安装命令跟安装pcre的类似,最后也是通过检查版本来判断是否安装成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo su
# wget http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz
# tar zxvf pcre-8.35.tar.gz
# cd pcre-8.35
# ./configure
# make && make install
# pcre-config --version
8.35
# wget http://nginx.org/download/nginx-1.10.0.tar.gz
# tar zxvf nginx-1.10.0.tar.gz
# cd nginx-1.10.0
# /configure --prefix=/usr/local/webserver/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.35
# make
# make install
# /usr/local/webserver/nginx/sbin/nginx -v
nginx version: nginx/1.10.0

安装完nginx之后,启动nginx,出现了以下错误。

bind-fail

这是因为nginx默认是监听80端口,而我的Linux上80端口已经被其他程序占用了,解决办法很简单,到nginx的配置文件中修改端口即可,这里我改成了3000端口。

port-3000

继续启动nginx,在浏览器访问0.0.0.0:3000,看到如下页面,才能说明nginx的安装真的成功了,可以用了。

welcome

完成nginx的安装之后。开始来写个简单的服务器方便后面测试。我使用NodeJS写了一个简单的服务器,代码如下所示,很简短(由于只是简单测试,所以就暂时不用Java这么庞大的东西了,方便说明实验过程)。它创建了4个服务器,分别监听8000、8001、8002、8003这四个端口。为了方便后面的测试,我设定每个服务器都响应不同的内容。监听8000端口的服务器对任何请求都响应文本"There is Server-1.",监听8001端口的服务器响应文本"There is Server-2.",以此类推。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var http = require('http');

http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('There is Server-1.');
}).listen(8000, () => {
console.log('server listen on 8000');
});

http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('There is Server-2.');
}).listen(8001, () => {
console.log('server listen on 8001');
});

http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('There is Server-3.');
}).listen(8002, () => {
console.log('server listen on 8002');
});

http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('There is Server-4.');
}).listen(8003, () => {
console.log('server listen on 8003');
});

有了服务器之后我们可以开始来配置nginx了。主要有两个地方需要配置,如下截图。配置upstream,在里面加入4个服务器的IP地址和端口号。配置location,将请求转发到upstream配置的服务器上。

upstream

location

完成配置之后就可以开始简单测试了。重启nginx,继续访问0.0.0.0:3000,如下截图所示,页面内容不再是刚才那个欢迎界面了,变成了NodeJS服务器提供的内容了。刷新几次页面,会发现页面的内容会发生变化,例如"There is Server-1."变成"There is Server-2.",说明nginx配置成功,能够正常工作。

nginx-proxy

接下来继续探究nginx的负载分配策略。在重复刷新浏览器页面的时候,页面中数字的变化是大概是1、2、3、4这种顺序,大致可以猜想nginx默认是按照轮询的方式进行负载分配的。为了能够更具体地测试和统计相关数据,我用Python写了下面一个简单的爬虫程序。爬虫程序会访问0.0.0.0:3000,把响应的内容输出,根据响应内容判断响应是来自哪个服务器并统计次数,最后把统计结果输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import urllib

url = 'http://0.0.0.0:3000/'
times = 100
server1 = 0
server2 = 0
server3 = 0
server4 = 0

while times > 0:
text = urllib.urlopen(url).read()
if (text[-2] == '1'):
server1 += 1
if (text[-2] == '2'):
server2 += 1
if (text[-2] == '3'):
server3 += 1
if (text[-2] == '4'):
server4 += 1
print(text)
times -= 1

print("Server-1: " + str(server1))
print("Server-2: " + str(server2))
print("Server-3: " + str(server3))
print("Server-4: " + str(server4))

通过调整程序中的times参数可以调整爬虫访问网站的次数,我进行了100次、1000次、10000次的测试,得到的结果如下。可以发现,四个服务器的访问次数都很均衡,所有请求都平摊到各个服务器上去了,这也说明了nginx可以很好地实现负载均衡。

times-100

times-1000

times-10000

输出每个响应的内容得到的结果如下,由于输出实在太长了,所以我这里只做了部分截图。由输出可以看出,nginx默认的负载均衡策略确实是轮询,访问的顺序基本都是按照1、2、3、4、1、2、3、4、...顺序地访问。

spider-print

如何控制策略?修改均衡策略其实很简单,只要在配置文件中修改即可,下面是简单示例,将均衡策略改为按照权重分配,weight越大的服务器分配到的负载越大,最后标注backup的服务器只有等到其他非backup服务器忙或者down的时候才会使用。

1
2
3
4
5
6
upstream nodeservers {
server 127.0.0.1:8000 weight = 1;
server 127.0.0.1:8001 weight = 2;
server 127.0.0.1:8002 weight = 3;
server 127.0.0.1:8003 backup;
}

什么是session stiky?是指当用户发出第一个request后,负载均衡器动态的把该用户分配到某个节点,并记录该节点的路由,以后该用户的所有request都会被绑定这个路由,用户只会与该服务器发生交互,使得session不会丢失。不采用session stiky的话可能造成用户session丢失,而session丢失的话就会带来很多问题,比如用户登录状态丢失等问题。如果不用session stiky的话可以使用session复制的方法来解决,即在多个节点之间复制session,实现不同服务器之间的用户状态同步。

2、Redis 安装

redis的安装过程如下,比较简单。下载tar包解压之后通过makefile编译,完成之后在src文件夹下即可找到服务器和客户端两个可执行程序,redis.conf是配置文件。到这里算是完成了,可以自己再把它们添加到环境变量,就不用每次都进来src文件夹下面启动程序了。

1
2
3
4
5
6
7
$ wget http://download.redis.io/releases/redis-3.2.5.tar.gz
$ tar xzf redis-3.2.5.tar.gz
$ cd redis-3.2.5
$ make
$ cd src
$ ./redis-server redis.conf
$ ./redis-cli

启动redis服务器,如下所示。看到以下输出说明安装成功,能够正常启动服务器。

redis server

我们再启动一个客户端,使用redis cli命令启动。如下所示,启动之后输入ping进行测试,输出PONG的话表明服务器能够正常工作,客户端可以连接到服务器。

ping

redis-cli下面我们可以直接对服务器进行配置,下面截图简单演示了配置的过程。其实就是简单地输入一下配置命令,没有什么特别的地方。还有另外一种方式是通过修改redis.conf文件来进行配置。

cli-config

然后是操作主要类型的数据。下面我在redis-cli下面使用命令简单操作了string、hashmap、set这几种数据类型,其他数据类型的操作基本相似,就只是命令不同而已,查阅文档之后可以很顺利完成。

string

hashmap

set

最后,简单了解了一下Redis Sentinel的知识。Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。它的主要功能有以下几点:

  • 不时地监控redis是否按照预期良好地运行;
  • 如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
  • 能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。

大概知道了Redis Sentinel是个什么东西,关于如何配置Sentinel,这里暂时不做深究。

3、共享数据缓存

在实验1的基础上,每个应用都使用同一 Redis 缓存,验证数据共享。首先将实验1中的代码进行小小修改,让它支持redis。修改代码后,服务器接收到请求之后会先检查redis里面是否有"tmp_string_key"的缓存,如果有的话就将其值读取出来响应给客户端,没有的话就设置一个。下面截取的是服务器1的代码,其他3个服务器的代码也是类似的改法。

代码修改

然后启动redis服务器、启动实验服务器、启动nginx。继续使用之前的爬虫来进行验证。输出的信息如下,所有的服务器相应的文本内容都是服务器4缓存的值,证明缓存数据是共享的。即第一次请求被转发到服务器4,然后服务器4设置了一个缓存键值对,后续不管请求被转发到哪个服务器,都会共享使用这个键值对,所以爬虫每次请求得到的内容都是一样的。

value

实验到此结束。

分享到