srs流媒体服务器集群搭建

单台服务器做直播,总归有单点风险,利用SRS的[Forward机制][1] + [Edge Server][2]设计,可以很容易搭建一个大规模的高可用集群,示意图如下

源站服务器集群:origin server cluster,可以借助forward机制,仅用少量的服务器,专用于处理推流请求。

边缘服务器集群:edge server cluster,可以用N台机器,从源站拉流,用于较大规模的实时播放。

源站前置负载均衡(硬件或软件负载均衡都行),上图中用haproxy来实现tcp的软负载均衡。

边缘服务器前置反向代理(比如:nginx),用于提供统一的播放地址,同时解决跨域问题,给客户端拉流播放。

这样架构的好处有以下:

  • 不管是源站集群,还是连缘服务器集群,均可水平扩展,理论上没有上限。
  • 源站可以仅用较少的机器,比如2主2从,就能实现一个高可用且性能尚可的集群(如果业务量不大,连slave server都可以省掉)
  • 边缘服务器集群,可以根据实际用户量随时调整规模,另外hls切片,可以放在edge server上切片,减轻源站服务器压力。

开始实战,博主2台服务器(centos 7.x),只能在每个虚拟机上用不同的端口启动多个srs实例,模拟master/slave/edge server (注:大家根据实际情况,将下面的ip换成自己真实的ip地址)

  • 执行此命令拉取srs源文件wget http://ossrs.net/srs.release/releases/files/SRS-CentOS6-x86_64-latest.zip && unzip -q SRS-CentOS6-x86_64-latest.zip
  • srs文件夹路径/mnt/srs/SRS-CentOS6-x86_64-2.0.263
  • 进入SRS文件夹内执行命令sudo bash INSTALL编译源文件
  • srs编译后配置文件路径/usr/local/srs/conf
  • 在conf下新建pm文件夹mkdir pm,后面所有自己的集群配置文件都会放入此文件夹

master配置

配置:/usr/local/srs/conf/pm/master.conf

listen              1945;
max_connections     1000;
pid                 ./objs/pids/srs.master.pid
srs_log_tank        file;
srs_log_file        ./objs/logs/master/srs.master.log;

http_api {
    enabled         on;
    listen          1995;
}

http_server {
    enabled         on;
    listen          8180;
    dir             ./objs/nginx/html;
}

stats {
    network         0;
    disk            sda sdb xvda xvdb;
}

vhost __defaultVhost__ {
        forward        172.**.***.71:1946 172.**.***.71:1947 172.**.***.73:1946 172.**.***.73:1947;
}

注:最后一段的forward,表示将视频流转发到4台slave服务器

slave配置

配置:/usr/local/srs/conf/pm/slave1.conf、/usr/local/srs/conf/pm/slave2.conf

slave1:

listen              1946;
max_connections     1000;
pid                 ./objs/pids/srs.slave1.pid
srs_log_tank        file;
srs_log_file        ./objs/logs/slave/srs.slave1.log;

http_api {
    enabled         on;
    listen          1996;
}

http_server {
    enabled         on;
    listen          8181;
    dir             ./objs/nginx/html;
}

stats {
    network         0;
    disk            sda sdb xvda xvdb;
}

vhost __defaultVhost__ {
}

slave2:

listen              1947;
max_connections     1000;
pid                 ./objs/pids/srs.slave2.pid
srs_log_tank        file;
srs_log_file        ./objs/logs/slave/srs.slave2.log;

http_api {
    enabled         on;
    listen          1997;
}

http_server {
    enabled         on;
    listen          8182;
    dir             ./objs/nginx/html;
}

stats {
    network         0;
    disk            sda sdb xvda xvdb;
}

vhost __defaultVhost__ {
}

edge配置

配置:/usr/local/srs/conf/pm/edge1.conf、/usr/local/srs/conf/pm/edge2.conf

edge1:

listen              1948;
max_connections     1000;
pid                 ./objs/pids/srs.edge1.pid
srs_log_tank        file;
srs_log_file        ./objs/logs/edge/srs.edge1.log;

http_api {
    enabled         on;
    listen          1998;
}

http_server {
    enabled         on;
    listen          8183;
    dir             ./objs/nginx/html;
}

stats {
    network         0;
    disk            sda sdb xvda xvdb;
}

vhost __defaultVhost__ {

    http_remux{
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
        hstrs       on;
    }

    hls{
        enabled         on;
        hls_path        ./objs/nginx/html;
        hls_fragment    3;
        hls_window      60;
    }

    mode            remote;
    origin          172.**.***.71:1945 172.**.***.71:1946 172.**.***.71:1947 172.**.***.73:1945 172.**.***.73:1946 172.**.***.73:1947;
}

edge2:

listen              1949;
max_connections     1000;
pid                 ./objs/pids/srs.edge2.pid
srs_log_tank        file;
srs_log_file        ./objs/logs/edge/srs.edge2.log;

http_api {
    enabled         on;
    listen          1999;
}

http_server {
    enabled         on;
    listen          8184;
    dir             ./objs/nginx/html;
}

stats {
    network         0;
    disk            sda sdb xvda xvdb;
}

vhost __defaultVhost__ {

    http_remux{
        enabled     on;
        mount       [vhost]/[app]/[stream].flv;
        hstrs       on;
    }

    hls{
        enabled         on;
        hls_path        ./objs/nginx/html;
        hls_fragment    3;
        hls_window      60;
    }

    mode            remote;
    origin          172.**.***.71:1945 172.**.***.71:1946 172.**.***.71:1947 172.**.***.73:1945 172.**.***.73:1946 172.**.***.73:1947;
}

注:最后一段的origin 将所有master、slave均做为视频源(origin server),如果播放时,edge发现自己机器上没有数据,会从origin配置的这些源站上去拉视频流。

每台虚拟机上,依次启动:slave1、slave2、master、edge1、edge2

cd /usr/local/srs
sudo ./objs/srs -c ./conf/pm/slave1.conf
sudo ./objs/srs -c ./conf/pm/slave2.conf
sudo ./objs/srs -c ./conf/pm/master.conf
sudo ./objs/srs -c ./conf/pm/edge1.conf
sudo ./objs/srs -c ./conf/pm/edge2.conf

启动成功后,建议先验证下是否工作正常:

  1. 测试前先打开阿里云安全组上述端口,并打开防火墙端口。

    • 阿里云开启安全组端口在此不做说明,具体方法请自行百度。
    • linux开启防火墙端口命令
      • firewall-cmd --zone=public --add-port=1945-1949/tcp --permanent
      • firewall-cmd --zone=public --add-port=1995-1999/tcp --permanent
      • firewall-cmd --zone=public --add-port=8180-8184/tcp --permanent
      • firewall-cmd --reload
  2. 可以用obs向每个master或slave推流试试,比如 rtmp://47.**.***.38:1945/live/testrtmp://101. **.***.215:1945/live/test,如果推流不报错,说明master/slave工作正常

    注意:此处使用的是外网ip,而配置中使用的是内网ip通信,务必区分!

  3. 然后用vlc播放器,验证从slave/edge这些服务器上拉流(比如 rtmp://47.**.***.38:1945/live/test 或 rtmp://101.**.***.215:1945/live/test,是否播放正常。

srs日志切割

这里说一下日志切割:在/etc/logrotate.d/目录下新建如下5个文件:

srs_edge1:

/usr/local/srs/objs/logs/edge/srs.edge1.log{
    copytruncate
    daily
    rotate 7
    missingok
    compress
    size 16M
}

srs_edge2:

/usr/local/srs/objs/logs/edge/srs.edge2.log{
    copytruncate
    daily
    rotate 7
    missingok
    compress
    size 16M
}

srs_master:

/usr/local/srs/objs/logs/master/srs.master.log{
    copytruncate
    daily
    rotate 7
    missingok
    compress
    size 16M
}

srs_slave1:

/usr/local/srs/objs/logs/slave/srs.slave1.log{
    copytruncate
    daily
    rotate 7
    missingok
    compress
    size 16M
}

srs_slave2:

/usr/local/srs/objs/logs/slave/srs.slave2.log{
    copytruncate
    daily
    rotate 7
    missingok
    compress
    size 16M
}

之后重启日志切割脚本:/usr/sbin/logrotate /etc/logrotate.conf

haproxy配置启动

如果上述2个步骤均验证ok,接下来就是如何配置haproxy

haproxy可做高性能负载均衡服务器,在其中一台机器上安装haproxy,这里我们使用47.**.***.38

  1. yum install haproxy (非常简单)

  2. vim /etc/haproxy/haproxy.cfg (修改配置文件)

    #---------------------------------------------------------------------  
    # Example configuration for a possible web application.  See the  
    # full configuration options online.  
    #
    #   http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
    #
    #---------------------------------------------------------------------
    
    #---------------------------------------------------------------------  
    # Global settings  
    #---------------------------------------------------------------------  
    global
        log         127.0.0.1 local2
    
        chroot      /var/lib/haproxy
        pidfile     /var/run/haproxy.pid
        maxconn     4000
        user        haproxy
        group       haproxy
        daemon
    
        stats socket /var/lib/haproxy/stats
    
    defaults
        mode            tcp
        log             global
        option          tcplog
        option          dontlognull
        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
    
    listen srs-cluster
        bind *:1935
        mode tcp
        balance roundrobin
        server master1 172.**.***.71:1945
        server master2 172.**.***.73:1945
    

注:关键是最后一段,把本机1935端口,转发到后端2台master服务器的1945端口。

  1. sudo systemctl restart haproxy (重启haproxy)

重启haproxy成功后,可以用obs推流到 rtmp://haproxy_server_ip:1935/live/test 试下推流是否正常,如果ok,可以尝试把其中一台master停掉,看看是否有影响。

nginx配置启动

最后是nginx出场了,ngnix的安装类似haproxy,yum install nginx 即可,关键是配置:

  • 新建配置文件nginx_srs.conf
  #user  nobody;
  worker_processes  1;
  #error_log  logs/error.log;
  #error_log  logs/error.log  notice;
  #error\_log  logs/error.log  info;
  #pid        logs/nginx.pid;


  events {
  worker_connections  1024;
  }
  http {
  include       mime.types;
  default_type  application/octet-stream;

  #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
  #                  '$status $body_bytes_sent "$http_referer" '
  #                  '"$http_user_agent" "$http_x_forwarded_for"';

  #access_log  logs/access.log  main;

  sendfile        on;
  #tcp_nopush     on;

  #keepalive_timeout  0;
  keepalive_timeout  65;

  #gzip  on;

  upstream srs {
  ip_hash;
  server 172.**.***.71:8183;
  server 172.**.***.71:8184;
  server 172.**.***.73:8183;
  server 172.**.***.73:8184;
  }

  server {
  listen       80;
  server_name  localhost;

  location ~ /* {
      proxy_pass http://srs;
      add_header Cache-Control no-cache;
      add_header Access-Control-Allow-Origin *;
  }

  location / {
      root   html;
      index  index.html index.htm;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
      root   html;
  }

  }

  include servers /*;
  }
  • nginx启动:/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx_srs.conf
  • nginx重启:/usr/local/nginx/sbin/nginx -s reload

注:新增一个upstream用于指定要转发的edge服务器节点,然后在location ~ /* 这里proxy_pass 指定upstream的名字即可(location ~ /* 切记要写在 location / 前面)。这样配置后,访问 http://nginx\_server\_ip/live/test.m3u8 理论上就能转到后端的edge服务器。

后记

发现一个比较严重的bug,nginx无法转发master与slave的流到edge,导致hls的切片无法获取。后续再排查,单独每个edge服务器可以正常工作。

nginx无法分发流的问题可以通过在haproxy配置分发解决,在实际测试中可以流可以得到分发并切片。

博客参考:[开源流媒体服务器SRS学习笔记(4) - Cluster集群方案][3]


  目录