python图表之pygal入门篇

pygal的简单使用

例子来自此书: 《Python编程从入门到实战》【美】Eric Matthes

pygal是一个SVG图表库。SVG是一种矢量图格式。全称Scalable Vector Graphics — 可缩放矢量图形。

用浏览器打开svg,可以方便的与之交互。

以下代码均在Jupyter Notebook中运行

模拟掷骰子

来看一个简单的例子。它模拟了掷骰子。

import random

class Die:
    """
    一个骰子类
    """
    def __init__(self, num_sides=6):
        self.num_sides = num_sides

    def roll(self):
        return random.randint(1, self.num_sides)

模拟掷骰子并可视化

import pygal

die = Die()
result_list = []
# 掷1000次
for roll_num in range(1000):
    result = die.roll()
    result_list.append(result)

frequencies = []
# 范围1~6,统计每个数字出现的次数
for value in range(1, die.num_sides + 1):
    frequency = result_list.count(value)
    frequencies.append(frequency)

# 条形图
hist = pygal.Bar()
hist.title = 'Results of rolling one D6 1000 times'
# x轴坐标
hist.x_labels = [1, 2, 3, 4, 5, 6]
# x、y轴的描述
hist.x_title = 'Result'
hist.y_title = 'Frequency of Result'
# 添加数据, 第一个参数是数据的标题
hist.add('D6', frequencies)
# 保存到本地,格式必须是svg
hist.render_to_file('die_visual.svg')

使用浏览器打开这个文件,鼠标指向数据,可以看到显示了标题“D6”, x轴的坐标以及y轴坐标。

可以发现,六个数字出现的频次是差不多的(理论上概率是1/6, 随着实验次数的增加,趋势越来越明显)

未分类

同时掷两个骰子

稍微改下代码就行,再实例化一个骰子

die_1 = Die()
die_2 = Die()

result_list = []
for roll_num in range(5000):
    # 两个骰子的点数和
    result = die_1.roll() + die_2.roll()
    result_list.append(result)

frequencies = []
# 能掷出的最大数
max_result = die_1.num_sides + die_2.num_sides

for value in range(2, max_result + 1):
    frequency = result_list.count(value)
    frequencies.append(frequency)

# 可视化
hist = pygal.Bar()
hist.title = 'Results of rolling two D6 dice 5000 times'
hist.x_labels = [x for x in range(2, max_result + 1)]
hist.x_title = 'Result'
hist.y_title = 'Frequency of Result'
# 添加数据
hist.add('two D6', frequencies)
# 格式必须是svg
hist.render_to_file('2_die_visual.svg')

从图中可以看出,两个骰子之和为7的次数最多,和为2的次数最少。因为能掷出2的只有一种情况 -> (1, 1);而掷出7的情况有(1, 6) , (2, 5), (3, 4), (4, 3), (5, 2), (6, 1)共6种情况,其余数字的情况都没有7的多,故掷得7得概率最大。

未分类

处理json数据–世界人口地图

需要用到人口数据

下载population.json: http://pan.baidu.com/s/1pKLB9N1 ,该数据来源于okfn.org这个网站

打开看下数据,其实这是个很长的列表,包含了许多国家从1960~2015年的人口数据。看第一数据,如下。后面的数据和第一个键都一样。

[ 
{
 "Country Name":"Arab World",
 "Country Code":"ARB",
 "Year":"1960",
 "Value":"92496099"
 },
...

只有四个键,其中Country Code指的是国别码,这里是3位的。Value就是人口数了。

import json

filename = r'F:Jupyter Notebookmatplotlib_pygal_csv_jsonpopulation.json'
with open(filename) as f:
    # json.load()可以将json文件转为Python能处理的形式,这里位列表,列表里是字典
    pop_data = json.load(f)

cc_populations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2015':
        country_name = pop_dict['Country Name']
        # 有些值是小数,先转为float再转为int
        population = int(float(pop_dict['Value']))
        print(country_name + ': ' + population)

上面的程序打印了2015年各个国家的人口数,当然要分析2014年的,代码中数字改改就行。

Arab World: 392168030
Caribbean small states: 7116360
Central Europe and the Baltics: 103256779
Early-demographic dividend: 3122757473.68203
East Asia & Pacific: 2279146555
...

需要注意的是,人口数据有些值是小数(不可思议)。人口数据类型是字符串str,如果直接转int,像’35435.12432’这样的字符串是不能强转位int的,必须先转为float,再丢失精度转为int。

获取两个字母的国别码

我们的数据中,国别码是三位的,而pygal的地图工具使用两位国别码。要使用pygal绘制世界地图。需要安装依赖包。

pip install pygal_maps_world就可以了

国别码位于i18n模块

from pygal_maps_world.i18n import COUNTRIES这样就导入了, COUNTRIES是一个字典,键是两位国别码,值是具体国家名。

key -> value
af Afghanistan
af Afghanistan
al Albania
al Albania
dz Algeria
dz Algeria
ad Andorra
ad Andorra
ao Angola

写一个函数,根据具体国家名返回pygal提供的两位国别码

def get_country_code(country_name):
    """
    根据国家名返回两位国别码
    """
    for code, name in COUNTRIES.items():
        if name == country_name:
            return code
    return None

世界人口地图绘制

先给出全部代码,需要用到World类

import json

from pygal_maps_world.i18n import COUNTRIES
from pygal_maps_world.maps import World
# 颜色相关
from pygal.style import RotateStyle
from pygal.style import LightColorizedStyle

def get_country_code(country_name):
    """
    根据国家名返回两位国别码
    """
    for code, name in COUNTRIES.items():
        if name == country_name:
            return code
    return None

filename = r'F:Jupyter Notebookmatplotlib_pygal_csv_jsonpopulation.json'
with open(filename) as f:
    pop_data = json.load(f)

cc_populations = {}
for pop_dict in pop_data:
    if pop_dict['Year'] == '2015':
        country_name = pop_dict['Country Name']

        # 有些值是小数,先转为float再转为int
        population = int(float(pop_dict['Value']))
        code = get_country_code(country_name)
        if code:
            cc_populations[code] = population

# 为了使颜色分层更加明显
cc_populations_1,cc_populations_2, cc_populations_3 = {}, {}, {}
for cc, population in cc_populations.items():
    if population < 10000000:
        cc_populations_1[cc] = population
    elif population < 1000000000:
        cc_populations_2[cc] = population
    else:
        cc_populations_3[cc] = population

wm_style = RotateStyle('#336699', base_style=LightColorizedStyle)
world = World(style=wm_style)
world.title = 'World Populations in 2015, By Country'
world.add('0-10m', cc_populations_1)
world.add('10m-1bn', cc_populations_2)
world.add('>1bn', cc_populations_3)
world.render_to_file('world_population_2015.svg')

有几个变量比较重要

  • cc_populations是一个dict,里面存放了两位国别码与人口的键值对。

  • cc_populations_1,cc_populations_2, cc_populations_3这是3个字典,把人口按照数量分阶梯,人口一千万以下的存放在cc_populations_1中,一千万~十亿级别的存放在cc_populations_2中,十亿以上的存放在cc_populations_3中,这样做的目的是使得颜色分层更加明显,更方便找出各个阶梯里人口最多的国家。由于分了三个层次,在添加数据的的时候也add三次,把这三个字典分别传进去。

  • world = World(style=wm_style)这是一个地图类,导入方法from pygal_maps_world.maps import World

  • wm_style = RotateStyle(‘#336699’, base_style=LightColorizedStyle)这里修改了pygal默认的主题颜色,第一个参数是16进制的RGB颜色,前两位代表R,中间两位代表G,最后两位代表B。数字越大颜色越深。第二个参数设置基础样式为亮色主题,pygal默认使用较暗的颜色主题,通过此方法可以修改默认样式。

中国大佬,No. 1

图中可以看出,分的三个颜色层次为。紫色系,十亿以上;蓝色系,一千万到十亿之间;绿色系,一千万一下。这三种颜色里面颜色最深的就对应了三个阶梯里人口最多的国家。

未分类

仔细观察,图中有些是空白的。并不是这些地方全部没人,而是我们的json数据中有些国家的名称与pygal中COUNTIES模块的国家名不对应导致。这算是一个遗憾,不过可以修改get_country_code函数,使得遇到不对应的国家名时,不返回None。对于这些国家,查阅COUNTRIES的代码,找出对应的国别码,返回之,应该就Ok了。比如下面这样

# 传入的参数country_name是json数据中的,可能与COUNTRIES里面的国家名不一致,按照上面的代码直接就返回None,导致绘图时这个国家的人口数据空白
if country_name == 'Yemen, Rep':
    return 'ye'

不过我懒得试了233

使用Web API分析数据

以GitHub为例,我想查看最受欢迎的Python库。以stars排序。

访问: https://api.github.com/search/repositories?q=language:python&sort=stars 就可查看。数据大概长这样

{
  "total_count": 1767997,
  "incomplete_results": false,
  "items": [{
     {
       "id": 21289110,
      "name": "awesome-python",
      "full_name": "vinta/awesome-python",
      "owner": {
        "login": "vinta",
        ...
          },
       ...    
       "html_url": "https://github.com/vinta/awesome-python",
        ...
          "stargazers_count": 35234,
        ...

  }, {...}
      ...]
}

第三个数据,items。里面是得到stars最多的top 30。name即库名称,owner下的login是库的拥有者,html_url是该库的网址(注意owner下也有个html_url。不过那个是用户的GitHub网址,我们要定位到该用户的具体这个库,所以不要用owner下的html_url),stargazers_count至关重要,所得的stars数目。

顺便说下第一个键total_count,表示Python语言的仓库的总数;第二个键,incomplete_results,表示响应的值不完全,一般来说是false,表示响应的数据完整。

import requests

url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
response = requests.get(url)
# 200为响应成功
print(response.status_code, '响应成功!')
response_dict = response.json()

total_repo = response_dict['total_count']
repo_list = response_dict['items']
print('总仓库数: ', total_repo)
print('top', len(repo_list))
for repo_dict in repo_list:
    print('nName: ', repo_dict['name'])
    print('Owner: ', repo_dict['owner']['login'])
    print('Stars: ', repo_dict['stargazers_count'])
    print('Repo: ', repo_dict['html_url'])
    print('Description: ', repo_dict['description'])

其实像这样已经得到结果了

200 响应成功!
总仓库数:  1768021
top 30

Name:  awesome-python
Owner:  vinta
Stars:  35236
Repo:  https://github.com/vinta/awesome-python
Description:  A curated list of awesome Python frameworks, libraries, software and resources

Name:  httpie
Owner:  jakubroztocil
Stars:  30149
Repo:  https://github.com/jakubroztocil/httpie
Description:  Modern command line HTTP client – user-friendly curl alternative with intuitive UI, JSON support, syntax highlighting, wget-like downloads, extensions, etc.  https://httpie.org

Name:  thefuck
Owner:  nvbn
Stars:  28535
Repo:  https://github.com/nvbn/thefuck
Description:  Magnificent app which corrects your previous console command.
...

可视化一下当然会更加直观。

pygal可视化数据

代码不是很难,有一个plot_dict比较关键,这是鼠标放在条形图上时,会显示出来的数据,键基本上都是固定写法,xlink里面时仓库地址,只要点击,浏览器就会新开一个标签跳转到该页面了!

import requests

import pygal
from pygal.style import LightColorizedStyle, LightenStyle

url = 'https://api.github.com/search/repositories?q=language:python&sort=stars'
response = requests.get(url)
# 200为响应成功
print(response.status_code, '响应成功!')
response_dict = response.json()

total_repo = response_dict['total_count']
repo_list = response_dict['items']
print('总仓库数: ', total_repo)
print('top', len(repo_list))

names, plot_dicts = [], []
for repo_dict in repo_list:
    names.append(repo_dict['name'])
    # 加上str强转,因为我遇到了'NoneType' object is not subscriptable (: 说明里面有个没有此项, 是NoneType
    plot_dict = {
        'value' : repo_dict['stargazers_count'],
        # 有些描述很长很长,选最前一部分
        'label' : str(repo_dict['description'])[:200]+'...',
        'xlink' : repo_dict['html_url']
    }
    plot_dicts.append(plot_dict)

# 改变默认主题颜色,偏蓝色
my_style = LightenStyle('#333366', base_style=LightColorizedStyle)
# 配置
my_config = pygal.Config()
# x轴的文字旋转45度
my_config.x_label_rotation = -45
# 隐藏左上角的图例
my_config.show_legend = False
# 标题字体大小
my_config.title_font_size = 30
# 副标签,包括x轴和y轴大部分
my_config.label_font_size = 20
# 主标签是y轴某数倍数,相当于一个特殊的刻度,让关键数据点更醒目
my_config.major_label_font_size = 24
# 限制字符为15个,超出的以...显示
my_config.truncate_label = 15
# 不显示y参考虚线
my_config.show_y_guides = False
# 图表宽度
my_config.width = 1000

# 第一个参数可以传配置
chart = pygal.Bar(my_config, style=my_style)
chart.title = 'Most-Starred Python Projects on GitHub'
# x轴的数据
chart.x_labels = names
# 加入y轴的数据,无需title设置为空,注意这里传入的字典,
# 其中的键--value也就是y轴的坐标值了
chart.add('', plot_dicts)
chart.render_to_file('most_stars_python_repo.svg')

看下图,chrome浏览器里显示效果。总感觉config里面有些设置没有起到作用, x、y轴的标签还是那么小orz…不过plot_dict里面的三个数据都显示出来了,点击即可跳转。

未分类

好了,就折腾这么多吧,这个库也不是特别大众的…

nginx和php-fpm连接超时之解决方法

前言

现在线上系统的架构大致是这样的,除去cache的proxy机器外,还有项目的nginx proxy机器,后面跟nginx webserver + php-fpm。有时候,会看到proxy nginx的日志里面会有各种异常状态码,比如499,502,504等,这些都是什么情况导致的呢?

架构示意

nginx proxy => nginx webserver => php-fpm

状态码说明

499:客户端(或者proxy)主动断开连jie502:网关错误(Bad Gateway)504:网关超时:(Gateway Timeout)

一、proxy和webserver不能连接

1.1 proxy_pass ip不存在

这时候会重复发送arp解析协议,约3秒后超时,proxy返回码为502。

1.2 proxy_pass ip存在

  • webserver机器上端口上没有对应服务;

webserver所在机器的内核会直接返回RESET包,没有额外超时,proxy返回码为502。

  • webserver机器端口上有服务,但是iptables DROP了proxy的包;

因为webserver drop(iptables -I INPUT -s xxx.xxx.xxx.xxx -j DROP)了proxy的包,proxy会TCP连接不断重试,默认会重试60秒后proxy返回码504,这个重试时间60秒由参数proxy_connect_timeout指定,重试间隔就是TCP的重试间隔(1,2,4…)。

如果在超时之前,客户端主动关闭连接(比如停止浏览器的请求),这个时候proxy会记录 499状态码,而且$request_time
记录的是proxy已经处理的时间,而$upstream_response_time为-。客户端主动关闭后,proxy也不会再向webserver发送重试请求。

但是如果你在proxy配置了proxy_ignore_client_abort on,那么即便客户端主动关闭,proxy还是会不停的发送重试请求到webserver,直至超时,记录的状态码为webserver返回的状态码。

  • webserver机器端口有服务,但是iptables reject了proxy的包。

因为webserver reject(iptables -I INPUT -s xxx.xxx.xxx.xxx -j REJECT)了proxy的包,与drop不同之处在于,这个时候webserver会返回一个端口不可达的ICMP包给proxy,proxy会重试一次后返回 502 给客户端,超时时间约为1秒。

二、proxy和webserver连接正常(请求时间过长)

proxy的nginx.conf中的proxy_read_timeout=60webserver的nginx.conf中fastcgi_read_timeout=300php-fpm中的 request_terminate_timeout=120

未分类

nginx.conf配置文件

2.1 php执行时间超过proxy的proxy_read_timeout:

假设php-fpm有一个test.php执行时间为100秒,超过了默认的proxy_read_timeout=60,则到1分钟后proxy会关闭到webserver的连接,webserver记录的返回码为499,proxy的返回码为 504,客户端看到的返回码也就是 504。

关于proxy_read_timeout要多说一句,在nginx文档中可以看到这个参数的含义是:

The timeout is set only between two successive read operations,not for the transmission of the whole response.

意思是说并非response的传输超时,而是两次读操作之间的间隔超时。比如在proxy中设置proxy_read_timeout=10,而测试的test.php 如下:

<?phpsleep(7);echo "haha";ob_flush();flush();sleep(7);echo "haha after 7s";?>

这整个请求的响应时间是14秒,其实是不会超时的,因为相邻两次读操作的间隔是7秒小于10秒。注意代码中的ob_flush()
和flush()两个函数,其中ob_flush()是为了刷php的缓存,flush()则是为了刷系统层面的缓存。将/etc/php5/fpm/php.ini中设置output_buffering=off,则可以不用调用ob_flush()了,但是flush()还是需要的。如果不flush的话,php会等到整个响应完成才会将数据返回给webserver,webserver再返回给proxy,在没有返回整个响应之前(14秒才能返回),超过了 proxy_read_timeout的10秒,此时,proxy会关闭和webserver的连接,导致出现504错误。为了这个测试test.php不超时,webserver的nginx还要加一个配置fastcgi_buffering off,因为虽然我们的php返回了数据了,但是webserver的nginx还是缓存了fastcgi的返回,导致没有及时将数据返回给proxy,从而超时。

未分类

php.ini文件

在如上面配置好后,可以发现,浏览器输出了hahahaha after 7s那么问题来了,这两个字符串是同时输出的,并没有像代码中那样隔了7秒,那这个问题是什么导致的呢?答案是proxy的nginx也有缓存配置,需要关闭才能看到先后输出两个字符串的效果。nginx proxy的缓存配置为proxy_buffering off,这样你就能看到先后输出两个字符串的效果了。

2.2 php执行时间超过webserver的fastcgi_read_timeout

设置fastcgi_read_timeout=10,test.php执行时间100秒,则10秒后webserver会关闭和PHP的连接,webserver记录日志的返回码为 504,proxy日志的返回码也是 504。

2.3 php执行时间超过php-fpm的request_terminate_timeout

设置request_terminate_timeout=5,test.php还是执行100秒,可以发现5秒之后,php-fpm会终止对应的php子进程,webserver日志的状态码为 404,proxy的日志的状态码也是 404。

注:经测试,在php-fpm模式中,php.ini中的max_execution_time参数没有什么效果。

三、关于文件数问题

Linux里面的一些限制参数可以通过ulimit -a查看,比如我的debian8.2系统的输出如下:

# ulimit -acore file size (blocks, -c) 0data seg size (kbytes, -d) unlimitedscheduling priority (-e) 0file size (blocks, -f) unlimitedpending signals (-i) 96537max locked memory (kbytes, -l) 64max memory size (kbytes, -m) unlimitedopen files (-n) 1000000pipe size (512 bytes, -p) 8POSIX message queues (bytes, -q) 819200real-time priority (-r) 0stack size (kbytes, -s) 8192cpu time (seconds, -t) unlimitedmax user processes (-u) 96537virtual memory (kbytes, -v) unlimitedfile locks (-x) unlimited

其中open files是一个进程可以同时打开的文件数,超过则会报too many open files错误,修改可以通过ulimit -n xxx来实现。而max user processes则是用户最多创建的进程数。

另外,系统允许打开的最大文件数在配置file-max中。

# cat /proc/sys/fs/file-max2471221

修改file-max可以通过# sysctl -w fs.file-max=1000000修改,永久生效需要在/etc/sysctl.conf中加入这行fs.file-max=1000000然后sysctl -p即可。

要针对用户限制文件数之类的,可以修改/etc/security/limits.conf,内容格式如下:

<domain> <type> <item> <value>## 比如限制 bob这个用户的一个进程同时打开的文件数## Example hard limit for max opened filesbob hard nofile 4096## Example soft limit for max opened filesbob soft nofile 1024

nginx配置中的worker_rlimit_nofile可以配置为open files这个值。

未分类

ulimit -a命令

未分类

sysctl.conf文件

nginx upstream 容错解决“nginx upstream timed out”错误

未分类

有相关人员在调查在服务器日志中发现的有关nginx错误:

Upstream timed out (110: Connection timed out) while reading response header from upstream

这个nginx超时错误位于一个nginx – apache代理服务器中nginx upstream 容错,其中nginx服务所有静态内容和apache所有动态。

Nginx Upstream Timed Out方案

调查错误,并尝试了一些修复后,发现这个错误可能会发生在两种情况:

1)Nginx作为代理

尝试在proxy_read_timeout虚拟主机配置中添加选项,应该如下所示:

nginx upstream 高可用proxy_read_timeout 150;

将其放置在您的根位置配置中:

位置 / {

proxy_read_timeout 150;

}

2)Nginx作为具有php-fpm或其他应用程序的独立服务器。

如果是这种情况,请尝试添加fastcgi_read_timeout选项:

fastcgi_read_timeout 150;

使用php-fpm配置,应该如下所示:

nginx cache位置?* .php $ {

包括fastcgi_params;

fastcgi_index index.php

fastcgi_read_timeout 150;

fastcgi_pass 127.0.0.1:9000;

fastcgi_param _FILENAME $ document_root $ fastcgi__name;

}

在这两种情况下,只需重新启动nginx即可应用更改。

GDCA一直以“构建网络信任体系,服务现代数字生活”的宗旨,致力于提供全球化的数字证书认证服务。其自主品牌——信鉴易?TrustAUTHSSL证书系列,为涉足互联网的企业打造更安全的生态环境,建立更具公信力的企业网站形象。

阿里云 debian 下 apt-get 搭建 nginx+php环境

1. 更新apt-get源

apt-get update

2. 安装Nginx

apt-get install nginx

nginx相关操作

service nginx start
service nginx restart
service nginx stop

3. 安装php

apt-get install php5-fpm php5-gd php5-mysql php5-memcache php5-curl

4. 配置Nginx让其支持php

cd /etc/nginx/conf.d #进入nginx虚拟站点配置目录
vi xxx.com.conf #创建域名配置文件

然后把下面的代码拷贝进去

server {
    listen 80;
    server_name phpmyadmin.xxx.com;
    root /home/wwwroot/phpMyadmin;
    index index.php;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ .php$ {
        include snippets/fastcgi-php.conf;
    #
    #   # With php5-cgi alone:
    #   fastcgi_pass 127.0.0.1:9000;
    #   # With php5-fpm:
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

附上nginx方向代理到nodeJs的配置

server{
    listen 80;
    server_name open.xxx.com;
    access_log off;
    location / {
        #proxy_cache_key "$scheme://$host$request_uri";
        #proxy_cache cache_one;
        #proxy_cache_valid  200 304 3h;
        #proxy_cache_valid 301 3d;
        #proxy_cache_valid any 10s;
        proxy_set_header   X-Real-IP  $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        #proxy_set_header   Referer http://xxxx;
        proxy_set_header   Host $host;
        #proxy_hide_header Set-Cookie;
        proxy_pass http://xx.xx.xx.xx:8080;
    }
}

CentOS 7:使用HAProxy实现Nginx负载均衡

HAProxy是一款功能强大、灵活好用的反向代理的开源软件,它提供了负载均衡、服务器代理的功能。HAProxy是Willy Tarreau使用C语言编写的,它支持SSL、压缩、keep-alive、自定义日志格式和header重写。

HAProxy是轻量级的负载均衡和代理服务软件,占用系统资源较少。很多大型的网站都在使用它,例如Github、StackOverflow。

下面我安装配置HAProxy做为两个Nginx服务器的负载均衡。一共需要使用3个服务器,在一台机器上安装HAProxy,另两台机器安装Nginx服务。

未分类

HAProxy的基本概念

4层和7层

HAProxy可以使用两种模式运行:TCP 4层模式和HTTP 7层模式。TCP模式:HAProxy把原始TCP数据包从客户端转向到应用服务器;HTTP模式:解析http请求,然后转向到web服务器。我们将使用HTTP 7层模式。

负载均衡算法

HAProxy使用负载均衡算法决定把请求发送给哪个服务器,使用的算法:
Roundrobin-轮流算法
这是最简单的负载均衡算法。对每个新连接,总是下一个后端服务器处理。如果到达最后一个后端服务器,从头开始。

Lastconn

有最少连接的后端服务器处理新请求。当请求量较大时非常好。

Source

根据客户端IP决定哪个后端服务器处理。如果IP1是server1处理,那么这个IP1的所有请求都由server1处理。根据IP地址的哈希值决定后端服务器。

系统要求

3个CentOS 7服务器:

  • 处理负载均衡的HAProxy服务器:192.168.0.101

  • Nginx1服务器:192.168.0.108

  • Nginx2服务器:192.168.0.109

第一步

编辑HAProxy服务器(102.168.0.101)的/etc/hosts:

vim /etc/hosts

添加Nginx1和Nginx2的主机名:

192.168.0.108    nginx1.your_domain.com     nginx1
192.168.0.109    nginx2.your_domain.com     nginx2

保存退出。

同样,编辑两个Nginx服务器的/etc/hosts,添加:

192.168.0.101    loadbalancer

在两个Nginx服务器上都要设置。

第二步

在HAProxy服务器上安装HAProxy:

# yum update
# yum install haproxy

haproxy的配置文件位于/etc/haproxy/。为了防止出错,先备份原始配置文件:

# cd /etc/haproxy/
# mv haproxy.cfg haproxy.cfg.backup

编辑配置文件:

# vim haproxy.cfg

写入如下内容:

#---------------------------------------------------------------------
# 全局设置
#---------------------------------------------------------------------
global
log         127.0.0.1 local2     # 日志
chroot      /var/lib/haproxy
pidfile     /var/run/haproxy.pid
maxconn     4000                
user        haproxy             # Haproxy在haproxy用户和组下运行
group       haproxy
daemon
# 开启 stats unix socket
stats socket /var/lib/haproxy/stats
#---------------------------------------------------------------------
# 基本设置
#---------------------------------------------------------------------
defaults
mode                    http
log                     global
option                  httplog
option                  dontlognull
option http-server-close
option forwardfor       except 127.0.0.0/8
option                  redispatch
retries                 3
timeout http-request    10s
timeout queue           1m
timeout connect         10s
timeout client          1m
timeout server          1m
timeout http-keep-alive 10s
timeout check           10s
maxconn                 3000
#---------------------------------------------------------------------
# HAProxy Monitoring 配置
#---------------------------------------------------------------------
listen haproxy3-monitoring *:8080                # Haproxy Monitoring 的使用端口:8080
mode http
option forwardfor
option httpclose
stats enable
stats show-legends
stats refresh 5s
stats uri /stats                            # HAProxy monitoring的网址
stats realm Haproxy Statistics
stats auth testuser:test1234                # 登录Monitoring的用户和密码
stats admin if TRUE
default_backend app-main
#---------------------------------------------------------------------
# FrontEnd 配置
#---------------------------------------------------------------------
frontend main
bind *:80
option http-server-close
option forwardfor
default_backend app-main
#---------------------------------------------------------------------
# 使用roundrobin做为负载均衡算法
#---------------------------------------------------------------------
backend app-main
balance roundrobin                                    # 使用的负载均衡算法
option httpchk HEAD / HTTP/1.1rnHost: localhost    # 检查nginx服务器是否连通- 200状态码
server nginx1 192.168.0.108:80 check                  # Nginx1 
server nginx2 192.168.0.109:80 check                  # Nginx2
配置rsyslog

我们需要使用rsyslog记录HAProxy的日志,编辑rsyslog.conf配置文件,打开UDP的514端口:

# vim /etc/rsyslog.conf

去掉如下行的注释:

$ModLoad imudp
$UDPServerRun 514

如果你想指定特定IP,可以更改如下行:

$UDPServerAddress 127.0.0.1

保存退出。

创建rsyslog配置文件:

# vim /etc/rsyslog.d/haproxy.conf

写入如下内容:

local2.=info     /var/log/haproxy-access.log    # 访问日志
local2.notice    /var/log/haproxy-info.log      # haproxy执行信息

重启rsyslog:

# systemctl restart rsyslog

启动HAProxy:

# systemctl start haproxy
# systemctl enable haproxy

第三步

安装配置Nginx。

Nginx1和Nginx2服务器配置方法相同。

安装epel-release:

# yum install epel-release

安装Nginx:

# yum install nginx

创建index.html测试文件,Nginx1(192.168.0.108):

# cd /usr/share/nginx/html/
# echo "<h1>nginx1.your_domain.com</h1>" > index.html
Nginx2(192.168.0.109):
# cd /usr/share/nginx/html/
# echo "<h1>nginx2.your_domain.com</h1>" > index.html

启动Nginx服务:

# systemctl enable nginx
# systemctl start nginx

测试

测试nginx1、2,使用浏览器访问:

192.168.0.108
192.168.0.109

正常访问网站:http://192.168.0.101,如果有域名,指向这个IP。
登录HAProxy web管理页面:

http://192.168.0.101:8080/stats

你应该可以看到nginx、http请求的转发信息。

nginx使用nginx-rtmp-module模块实现直播间功能

系统环境

wujianjun@wujianjun-work ~ $ uname -a
Linux wujianjun-work 4.10.0-37-generic #41~16.04.1-Ubuntu SMP Fri Oct 6 22:42:59 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

软件环境

  • OBS(Open Broadcaster Software) v20.0.1 (Linux)

  • nginx version: nginx/1.13.6

  • built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)

  • built with OpenSSL 1.0.2g 1 Mar 2016

  • TLS SNI support enabled

  • configure arguments: Cwith-pcre=pcre-8.38 Cadd-module=nginx-rtmp-module-1.1.11

Nginx+obs安装及配置 安装obs

wujianjun@wujianjun-work ~ $ sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next
wujianjun@wujianjun-work ~ $ sudo apt-get update && sudo apt-get install ffmpeg
wujianjun@wujianjun-work ~ $ sudo apt-get install obs-studio
wujianjun@wujianjun-work ~ $ sudo add-apt-repository ppa:obsproject/obs-studio
wujianjun@wujianjun-work ~ $ sudo apt-get update && sudo apt-get install obs-studio

nginx加装rtmp模块

nginx-rtmp-module ( https://github.com/arut/nginx-rtmp-module )

wujianjun@wujianjun-work ~ $ sudo apt-get install build-essential
wujianjun@wujianjun-work ~ $ wget wget http://nginx.org/download/nginx-1.13.6.tar.gz
wujianjun@wujianjun-work ~/nginx-1.13.6 $ wget https://github.com/arut/nginx-rtmp-module/archive/v1.1.11.tar.gz
wujianjun@wujianjun-work ~/nginx-1.13.6 $ tar -xvf v1.1.11.tar.gz
wujianjun@wujianjun-work ~/nginx-1.13.6 $ wget http://jaist.dl.sourceforge.net/project/pcre/pcre/8.38/pcre-8.38.tar.gz
wujianjun@wujianjun-work ~/nginx-1.13.6 $ tar -xvf pcre-8.38.tar.gz
wujianjun@wujianjun-work ~/nginx-1.13.6 $ ls -all
总用量 748
drwxr-xr-x 9 wujianjun wujianjun 4096 10月 15 11:39 .
drwxr-xr-x 63 wujianjun wujianjun 4096 10月 15 11:33 ..
drwxr-xr-x 6 wujianjun wujianjun 4096 10月 15 11:33 auto
-rw-r--r-- 1 wujianjun wujianjun 282456 10月 10 23:22 CHANGES
-rw-r--r-- 1 wujianjun wujianjun 430416 10月 10 23:22 CHANGES.ru
drwxr-xr-x 2 wujianjun wujianjun 4096 10月 15 11:33 conf
-rwxr-xr-x 1 wujianjun wujianjun 2502 10月 10 23:22 configure
drwxr-xr-x 4 wujianjun wujianjun 4096 10月 15 11:33 contrib
drwxr-xr-x 2 wujianjun wujianjun 4096 10月 15 11:33 html
-rw-r--r-- 1 wujianjun wujianjun 1397 10月 10 23:22 LICENSE
drwxr-xr-x 2 wujianjun wujianjun 4096 10月 15 11:33 man
drwxrwxr-x 6 wujianjun wujianjun 4096 2月 13 2017 nginx-rtmp-module-1.1.11
drwxr-xr-x 7 wujianjun wujianjun 4096 11月 23 2015 pcre-8.38
-rw-r--r-- 1 wujianjun wujianjun  49 10月 10 23:22 README
drwxr-xr-x 9 wujianjun wujianjun 4096 10月 15 11:33 src
wujianjun@wujianjun-work ~/nginx-1.13.6 $ ./configure --with-pcre=pcre-8.38 --add-module=nginx-rtmp-module-1.1.11
wujianjun@wujianjun-work ~/nginx-1.13.6 $ make && sudo make install
wujianjun@wujianjun-work ~/nginx-1.13.6 $ ls -all /usr/local/nginx/
总用量 24
drwxr-xr-x 6 root root 4096 10月 15 16:11 .
drwxr-xr-x 11 root root 4096 10月 15 16:11 ..
drwxr-xr-x 2 root root 4096 10月 15 16:11 conf
drwxr-xr-x 2 root root 4096 10月 15 16:11 html
drwxr-xr-x 2 root root 4096 10月 15 16:11 logs
drwxr-xr-x 2 root root 4096 10月 15 16:11 sbin

增加rtmp协议配置

wujianjun@wujianjun-work ~/nginx-1.13.6 $ sudo vi /usr/local/nginx/conf/nginx.conf

在nginx.conf文件末尾增加以下rtmp协议的配置

rtmp {
 server {
  listen 1935;
  chunk_size 4096;

  application live {
   live on;
   record off;
  }
 }
}

启动&测试

启动nginx

wujianjun@wujianjun-work ~/nginx-1.13.6 $ sudo /usr/local/nginx/sbin/nginx

启动OBS

打开刚安装的OBS软件,在来源处配置图像的推送来源(我这里选择窗口捕获),点击右下角”设置”,进行如下图配置流推送地址

未分类

配置完成后,点击”开始推流”

启动支持网络流播放的视频播放器(演示使用vlc播放器)

配置网络流播放的地址,如下图:

未分类

当点击”播放”后,稍等几秒,即可看到播放器显示了obs捕获的图像了。

未分类

由于视频流需要通过网络进行传输,所以直播图像会有几秒的延迟。

http访问直播视频

1、更改nginx.conf中配置,增加hls配置(hls是在流媒体服务器中用来存放流媒体的文件夹),再次hls所在目录设置为http协议访问目录即可,更改后的配置如下:

rtmp {
 server {
  listen 1935;
  chunk_size 4096;

  application live {
   live on;
   hls on;
   hls_path /usr/share/nginx/html/hls;
   hls_fragment 5s;
  }
 }
}

http {
 server {
 listen 80;
 .....
 location / {
   #root html;
   root /usr/share/nginx/html;
   index index.html index.htm;
 }
 .....
 }
}

注意:hls所在目录nginx的用户必须有写入权限。

2、obs软件配置录制流名称

在配置obs推送流URL的下方有一个设置”流名称”的地方,这里可以随意填写一个名称(我这里示例填入”test”)

3、重启一下nginx与obs软件,我们即可在手机浏览器中输入 http://ip/hls/test.m3u8 即可通过手机播放直播视频。(直播延迟有点大,后续出文章优化)

未分类

zabbix自定Nginx状态监控

监控效果

未分类

未分类

自定义监控脚本

#!/bin/bash
#author:51itinfo.com
## Active connections: 对后端发起的活动连接数
## Server accepts handled requests: accepts表示共处理了多少个连接,handled表示成功创建了 多少次握手(没有失败次数),requests表示总共处理了多少个请求
## Reading: Nginx 读取到客户端的 Header 信息数
## Writing: Nginx 返回给客户端的 Header 信息数
## Waiting: 开启 keep-alive 的情况下,这个值等于 active - ( reading + writing ), 意思是 Nginx 已经处理完成,正在等待下一次请求指令的驻留连接
## 在访问效率很高,请求很快被处理完毕的情况下,Waiting 数比较多是正常的。如果 reading + writing 数较多,则说明并发访问量很大,正在处理过程中

case $1 in
    active)
        curl -s http://127.0.0.1/nginx_status | awk '/Active/ {print $3}' ;;
    accepts)
        curl -s http://127.0.0.1/nginx_status | awk 'NR==3 {print $1}' ;;
    handled)
        curl -s http://127.0.0.1/nginx_status | awk 'NR==3 {print $2}' ;;
    requests)
        curl -s http://127.0.0.1/nginx_status | awk 'NR==3 {print $3}' ;;
    reading)
        curl -s http://127.0.0.1/nginx_status | awk '/Reading/ {print $2}' ;;
    writing)
        curl -s http://127.0.0.1/nginx_status | awk '/Writing/ {print $4}' ;;
    waiting)
        curl -s http://127.0.0.1/nginx_status | awk '/Waiting/ {print $6}' ;;
    *)
        echo "Usage: $0 { active | accepts | handled | requests | reading | writing | waiting }" ;;
esac

nginx 服务器重启命令,关闭

未分类

nginx -s reload  :修改配置后重新加载生效
nginx -s reopen  :重新打开日志文件
nginx -t -c /path/to/nginx.conf 测试nginx配置文件是否正确

关闭nginx:

nginx -s stop  :快速停止nginx
nginx -s quit  :完整有序的停止nginx

其他的停止nginx 方式:

ps -ef | grep nginx

kill -QUIT 主进程号 :从容停止Nginx kill -TERM 主进程号 :快速停止Nginx pkill -9 nginx :强制停止Nginx

启动nginx:

nginx -c /path/to/nginx.conf

平滑重启nginx:

kill -HUP 主进程号

Mac下Nginx端口被占用问题

配置PHP过程中看到nginx的日志文件大小达到800多MB,里面的日志内容多是:nginx: [emerg] bind() to 0.0.0.0:80 failed (48: Address already in use)等端口占用信息,不免好奇。

Mac默认的Apache已经stop,再无其他应用、程序占用80端口。事实上,netstat分析也显示没有其他进程占用,但结果就摆在眼前,仍在不停输出。通过搜索发现,可能是IPv6所致。于是带上–with-ipv6选项重新编译,并监听ipv6地址,设为ipv6only=on。重新reload后,转为报错:nginx: [emerg] bind() to [::]:80 failed (48: Address already in use)。看来,网上的解决方案无济于事。

逐渐尝试升级Nginx、换端口,问题依旧,只是提示信息改为8080、8081等。看来,问题并不出在ipv6等配置上。

升级过程中,偶然的发现,sudo launchctl unload org.nginx.plist后,似乎报错停止了。于是,恢复配置,reload后,进行了unload测试。果不其然,事实证实如此。问题完美解决。

nginx yii2 配置路径 500问题

困扰了一个上午的问题

nginx conf中将yii2项目的路径配置到 根目录不会出错。
但是 配置到 frontend/web下 时 访问 就会报错500 服务器内部错误

不死心的我 google了很久。。。。。。。

发现和php 的 open_basedir有关系

改了下。

未分类

更改 你要显示的路径

报错解决。