一、采集

SRS支持两种方式得到RTMP直播源。
一种是使用FFmpeg, 设备或其它方式将流推送到SRS。
另一种方式是SRS本身带采集功能。

采集(Ingest)指的是将文件(flv,mp4,mkv,avi,rmvb等等),
流(RTMP,RTMPT,RTMPS,RTSP,HTTP,HLS等等),设备等的数据,
转封装为RTMP流(若编码不是h264/aac则需要转码),推送到SRS。

采集基本上就是使用FFMPEG作为编码器,或者转封装器,将外部流主动抓取到SRS。

采集的部署实例参考:Ingest

1.1 应用场景

采集的主要应用场景包括:
1.虚拟直播:      
  将文件编码为直播流。可以指定多个文件后,SRS会循环播放。
2.RTSP摄像头对接:
  以前安防摄像头都支持访问RTSP地址,RTSP无法在互联网播放。
  可以将RTSP采集后,以RTMP推送到SRS,后面的东西就不用讲了。
3.直接采集设备:  
  SRS采集功能可以作为编码器采集设备上的未压缩图像数据,
  譬如video4linux和alsa设备,编码为h264/aac后输出RTMP到SRS。
4.将HTTP流采集为RTMP:
  有些老的设备,能输出HTTP的ts或FLV流,可以采集后转封装为RTMP,支持HLS输出。
总之,采集的应用场景主要是“SRS拉流”;
能拉任意的流,只要ffmpeg支持;
不是h264/aac都没有关系,ffmpeg能转码。

SRS默认是支持“推流”,即等待编码器推流上来,
可以是专门的编码设备,FMLE,ffmpeg,xsplit,flash等等。

如此,SRS的接入方式可以是“推流到SRS”和“SRS主动拉流”,
基本上作为源站的功能就完善了。

1.2 编译

Ingest需要在编译时打开:--with-ingest。参考:Build

Ingest默认使用自带的ffmpeg,也可以不编译ffmpeg,使用自己的编转码工具。
禁用默认的ffmpeg在编译时指定--without-ffmpeg即可。
参考:Build

1.3 配置

Ingest的配置如下:
vhost your_vhost {
    # ingest file/stream/device then push to SRS over RTMP.
    # the name/id used to identify the ingest, must be unique in global.
    # ingest id is used in reload or http api management.
    ingest livestream {
        # whether enabled ingest features
        # default: off
        enabled      on;
        # input file/stream/device
        # @remark only support one input.
        input {
            # the type of input.
            # can be file/stream/device, that is,
            #   file: ingest file specifies by url.
            #   stream: ingest stream specifeis by url.
            #   device: not support yet.
            # default: file
            type    file;
            # the url of file/stream.
            url     ./doc/source.200kbps.768x320.flv;
        }
        # the ffmpeg 
        ffmpeg      ./objs/ffmpeg/bin/ffmpeg;
        # the transcode engine, @see all.transcode.srs.com
        # @remark, the output is specified following.
        engine {
            # @see enabled of transcode engine.
            # if disabled or vcodec/acodec not specified, use copy.
            # default: off.
            enabled          off;
            # output stream. variables:
            # [vhost] current vhost which start the ingest.
            # [port] system RTMP stream port.
            output          rtmp://127.0.0.1:[port]/live?vhost=[vhost]/livestream;
        }
    }
}
ingest指令后面是ingest的id,全局需要唯一,用来标识这个ingest。
在reload/http-api管理时才知道操作的是哪个。
譬如,reload时用来检测哪些ingest更新了,需要通知那些已经存在的ingest,停止已经不存在的ingest。

其中,type指定了输入的几种类型:
file:    输入为文件,url指定了文件的路径。srs会给ffmpeg传递-re参数。
stream:  输入为流,url指定了流地址。
device:  暂时不支持。
engine: 指定了转码引擎参数:
enabled: 指定是否转码,若off或者vcodec/acodec没有指定,则不转码,使用ffmpeg-copy。
output: 输出路径。
         有两个变量可以使用:
           port为系统侦听的RTMP端口,
           vhost为配置了ingest的vhost。
其他参考转码的配置:FFMPEG

注意:engine默认为copy,当:
engine的enabled为off,没有开启转码engine,则使用copy。
engine的vcodec/acodec没有指定,则使用copy。
采集多个文件。
实现方法:
可以把输入文件变成文件列表。自己写工具实现采集列表。

二、Forward模式搭建小型集群

srs定位为直播服务器,其中一项重要的功能是forward,即将服务器的流转发到其他服务器。

备注:SRS的边缘RTMP参考Edge,支持访问时回源,为大规模并发提供最佳解决方案。
注意:edge可以从源站拉流,也可以将流转发给源站。
      也就是说,播放edge上的流时,edge会向源站拉流;
      推流到edge上时,edge会直接将流转发给源站。
注意:若只需要中转流给源站,不必用forward,直接使用edge模式即可。
      可以直接支持推流和拉流的中转,简单快捷。
      Forward应用于目标服务器是多个,譬如将一路流主动送给多路服务器;
      edge虽然配置了多台服务器,但是只用了一台,有故障时才切换。
注意:优先使用edge,除非知道必须用forward,才使用forward。

forward本身是用做热备,即用户推一路流上来,可以被SRS转发(或者转码后转发)到多个slave源站,
CDN边缘可以拉多个slave源站的流,实现故障热备的功能,构建强容错系统。

转发的部署实例参考:Usage: Forward

2.1 词汇

为了和edge方式区分,forward定义一次词汇如下:
master:主服务器,
        编码器推流到这个服务器,或者用ingest流到服务器。
        总之,master就是主服务器,负责转发流给其他服务器。
slave: 从服务器,主服务器转发流到这个服务器。

如果结合edge集群方式,一般而言master和slave都是origin(源站服务器),
edge边缘服务器可以从master或者slave回源取流。
实际上master和slave也可以是edge,但是不推荐,这种组合方式太多了,测试没有办法覆盖到。
因此,强烈建议简化服务器的结构,只有origin(源站服务器)才配置转发,edge(边缘服务器)只做边缘。

2.2 For Small Cluster

forward也可以用作搭建小型集群。架构图如下:

+-------------+    +---------------+
                               +-->+ Slave(1935) +->--+  Player(3000) +
                               |   +-------------+    +---------------+
                               |   +-------------+    +---------------+
                               |-->+ Slave(1936) +->--+  Player(3000) +
         publish       forward |   +-------------+    +---------------+
+-----------+    +--------+    |     192.168.1.6                       
|  Encoder  +-->-+ Master +-->-|                                       
+-----------+    +--------+    |   +-------------+    +---------------+
 192.168.1.3    192.168.1.5    +-->+ Slave(1935) +->--+  Player(3000) +
                               |   +-------------+    +---------------+
                               |   +-------------+    +---------------+
                               +-->+ Slave(1936) +->--+  Player(3000) +
                                   +-------------+    +---------------+
                                     192.168.1.7

2.3 下面是搭建小型集群的实例。

1. Encoder编码器

编码器使用FFMPEG推流。编码参数如下:

for((;;)); do\
    ./objs/ffmpeg/bin/ffmpeg \
    -re -i doc/source.200kbps.768x320.flv \
    -vcodec copy -acodec copy \
    -f flv -y rtmp://192.168.1.5:1935/live/livestream; \
done

2. SRS-Master服务器
SRS(192.168.1.5)的配置如下:

listen              1935;
pid                 ./objs/srs.pid;
max_connections     10240;
vhost __defaultVhost__ {
    gop_cache       on;
    forward         192.168.1.6:1935 192.168.1.6:1936 192.168.1.7:1935 192.168.1.7:1936;
}
源站的流地址播放地址是:rtmp://192.168.1.5/live/livestream

将流forward到两个边缘节点上。

3. SRS-Slave节点
Slave节点启动多个SRS的进程,每个进程一个配置文件,侦听不同的端口。

以192.168.1.6的配置为例,需要侦听1935和1936端口。

配置文件srs.1935.conf配置如下:

listen              1935;
pid                 ./objs/srs.1935.pid;
max_connections     10240;
vhost __defaultVhost__ {
    gop_cache       on;
}
配置文件srs.1936.conf配置如下:

listen              1936;
pid                 ./objs/srs.1936.pid;
max_connections     10240;
vhost __defaultVhost__ {
    gop_cache       on;
}
启动两个SRS进程:

nohup ./objs/srs -c srs.1935.conf >/dev/null 2>&1 &
nohup ./objs/srs -c srs.1936.conf >/dev/null 2>&1 &
播放器可以随机播放着两个流:

rtmp://192.168.1.6:1935/live/livestream
rtmp://192.168.1.6:1936/live/livestream
另外一个Slave节点192.168.1.7的配置和192.168.1.6一样。

4. 服务的流
此架构服务中的流为:
流地址 服务器 端口 连接数
rtmp://192.168.1.6:1935/live/livestream 192.168.1.6 1935 3000
rtmp://192.168.1.6:1936/live/livestream 192.168.1.6 1936 3000
rtmp://192.168.1.7:1935/live/livestream 192.168.1.7 1935 3000
rtmp://192.168.1.7:1936/live/livestream 192.168.1.7 1936 3000
这个架构每个节点可以支撑6000个并发,两个节点可以支撑1.2万并发。 还可以加端口,可以支持更多并发。

2.4. Forward VS Edge

Forward架构和CDN架构的最大区别在于,CDN属于大规模集群,
边缘节点会有成千上万台,源站2台(做热备),还需要有中间层。
CDN的客户很多,流也会有很多。
所以假若源站将每个流都转发给边缘,会造成巨大的浪费(有很多流只有少数节点需要)。

可见,forward只适用于所有边缘节点都需要所有的流。CDN是某些边缘节点需要某些流。

forward的瓶颈在于流的数目,假设每个SRS只侦听一个端口:
系统中流的数目 = 编码器的流数目 × 节点数目 × 端口数目
考虑5个节点,每个节点起4个端口,即有20个SRS边缘。编码器出5路流,则有20 * 5 = 100路流。

同样的架构,对于CDN的边缘节点来讲,系统的流数为用户访问边缘节点的流,
假设没有用户访问,系统中就没有流量。某个区域的用户访问某个节点上的流,
系统中只有一路流,而不是forward广播式的多路流。
另外,forward需要播放器随机访问多个端口,实现负载均衡,或者播放器访问api服务器,
api服务器实现负载均衡,对于CDN来讲也不合适(需要客户改播放器)。
总之,forward适用于小型规模的集群,不适用于CDN大规模集群应用。

2.5 高级应用

forward还可以结合hls和transcoder功能使用,即在源站将流转码,
然后forward到Slave节点,Slave节点支持rtmp同时切HLS。

因为用户推上来的流,或者编码器(譬如FMLE)可能不是h264+aac,
需要先转码为h264+aac(可以只转码音频)后才能切片为hls。

需要结合vhost,先将流transcode送到另外一个vhost,这个vhost将流转发到Slave。
这样可以只转发转码的流。
参考vhost,hls和transcoder相关wiki。

三、Edge边缘服务器

SRS的Edge提供访问时回源机制,在CDN/VDN等流众多的应用场景中有重大意义, 
forward/ingest方案会造成大量带宽浪费。
同时,SRS的Edge能对接所有的RTMP源站服务器, 
不像FMS的Edge只能对接FMS源站(有私有协议);
另外,SRS的Edge支持SRS源站的所有逻辑 (譬如转码,转发,HLS,DVR等等),
也就是说可以选择在源站切片HLS,也可以直接在边缘切片HLS。

备注:Edge一般负载高,SRS支持的并发足够跑满千兆网带宽了。

3.1 Edge的主要应用场景:

CDN/VDN大规模集群,客户众多流众多需要按需回源。
小规模集群,但是流比较多,需要按需回源。
骨干带宽低,边缘服务器强悍,可以使用多层edge,降低上层BGP带宽。

注意1.:
edge可以从源站拉流,也可以将流转发给源站。
也就是说,播放edge上的流时,edge会回源拉流;
推流到edge上时,edge会直接将流转发给源站。

注意2.:
若只需要中转流给源站,不必用forward,直接使用edge模式即可。
可以直接支持推流 和拉流的中转,简单快捷。
Forward应用于目标服务器是多个,譬如将一路流主动送给多路服务器;
edge虽然配置了多台服务器,但是只用了一台,有故障时才切换。

注意:优先使用edge,除非知道必须用forward,才使用forward。

3.2 概念

所谓边缘edge服务器,就是边缘直播缓存服务器,
配置时指定为remote模式和origin(指定一个或多个源站IP),
这个边缘edge服务器就是源站的缓存了。

当用户推流到边缘服务器时,边缘直接将流转发给源站。
譬如源站在北京BGP机房,湖南有个电信ADSL用户要推流发布自己的直播流,
要是直接推流到北京BGP可能效果不是很好,可以在湖南电信机房部署一个边缘,
用户推流到湖南边缘,边缘转发给北京源站BGP。

当用户播放边缘服务器的流时,边缘服务器看有没有缓存,若缓存了就直接将流发给客户端。 
若没有缓存,则发起一路回源链接,从源站取数据源源不断放到自己的缓存队列。
也就是说, 多个客户端连接到边缘时,只有一路回源。
这种结构在CDN是最典型的部署结构。
譬如北京源站, 在全国32个省每个省都部署了10台服务器,
一共就有320台边缘,假设每个省1台边缘服务器都有2000用户观看,那么就有64万用户,
每秒钟集群发送640Gbps数据;而回源链接只有320个,实现了大规模分发。

边缘edge服务器,实际上是解决大并发问题产生的分布式集群结构。
SRS的边缘可以指定多个源站,在源站出现故障时会自动切换到下一个源站,
不影响用户观看,具有最佳的容错性,用户完全不会觉察。

3.3 Config

edge属于vhost的配置,将某个vhost配置为edge后,
该vhost会回源取流(播放时)或者将流转发 给源站(发布时)。

vhost __defaultVhost__ {
    # the mode of vhost, local or remote.
    #       local: vhost is origin vhost, which provides stream source.
    #       remote: vhost is edge vhost, which pull/push to origin.
    # default: local
    mode            remote;
    # for edge(remote mode), user must specifies the origin server
    # format as: [:port]
    # @remark user can specifies multiple origin for error backup, by space,
    # for example, 192.168.1.100:1935 192.168.1.101:1935 192.168.1.102:1935
    origin          127.0.0.1:1935 localhost:1935;
    # for edge, whether open the token traverse mode,
    # if token traverse on, all connections of edge will forward to origin to check(auth),
    # it's very important for the edge to do the token auth.
    # the better way is use http callback to do the token auth by the edge,
    # but if user prefer origin check(auth), the token_traverse if better solution.
    # default: off
    token_traverse  off;
}
可配置多个源站,在故障时会切换到下一个源站。

3.4 集群配置

下面举例说明如何配置一个源站和集群。

源站配置,参考origin.conf:
listen              19350;
pid                 objs/origin.pid;
srs_log_file        ./objs/origin.log;
vhost __defaultVhost__ {
}

边缘配置,参考edge.conf:
listen              1935;
pid                 objs/edge.pid;
srs_log_file        ./objs/edge.log;
vhost __defaultVhost__ {
    mode            remote;
    origin          127.0.0.1:19350;
}

3.5 HLS边缘

Edge指的是RTMP边缘,也就是说,配置为Edge后,流推送到源站(Origin)时,Edge不会切片生成HLS。
HLS切片配置在源站,只有源站会在推流上来就产生HLS切片。
边缘只有在访问时才会回源(这个时候 也会生成HLS,但单独访问边缘的HLS是不行的)。

也就是说,HLS的边缘需要使用WEB服务器缓存,譬如nginx反向代理,squid,或者traffic server等。

3.6 下行边缘结构设计

下行边缘指的是下行加速边缘,即客户端播放边缘服务器的流,边缘服务器从上层或源站取流。
SRS下行边缘是非常重要的功能,需要考虑以下因素:
以后支持多进程时结构变动最小。
和目前所有功能的对接良好。
支持平滑切换,源站和边缘两种角色。
权衡后,SRS下行边缘的结构设计如下:

客户端连接到SRS
开始播放SRS的流
若流存在则直接播放。
若流不存在,则从源站开始取流。
其他其他流的功能,譬如转码/转发/采集等等。

核心原则是:
边缘服务器在没有流时,向源站拉取流。
当流建立起来后,边缘完全变成源站服务器,对流的处理逻辑保持一致。
支持回多个源站,错误时切换。这样可以支持上层服务器热备。
备注:RTMP多进程(计划中)的核心原则是用多进程作为完全镜像代理,
连接到本地的服务器 (源站或边缘),完全不考虑其他业务因素,透明代理。
这样可以简单,而且利用多CPU能力。 HTTP多进程是不考虑支持的,用NGINX是最好选择,
SRS的HTTP服务器只是用在嵌入式设备中, 没有性能要求的场合。

3.7 上行边缘结构设计

上行边缘指的是上行推流加速,客户端推流到边缘服务器,边缘将流转发给源站服务器。
考虑到下行和上行可能同时发生在一台边缘服务器,所以上行边缘只能用最简单的代理方式, 
完全将流代理到上层或源站服务器。也就是说,只有在下行边缘时,边缘服务器才会启用其他的功能,
譬如HLS转发等等。

上行边缘主要流程是:
客户端连接到SRS
开始推流到SRS。
开始转发到源站服务器。

EdgeState


边缘的状态图分析如下:
RTMP-HLS-latency

注意:这种细节的文档很难保持不变,以代码为准。

3.8 边缘的难点

RTMP边缘对于SRS来讲问题不大,主要是混合了reload和HLS功能的边缘服务器,会是一个难点。
譬如,用户在访问边缘上的HLS流时,是使用nginx反向代理回源,还是使用RTMP回源后在边缘切片? 
对于前者,需要部署srs作为RTMP边缘,nginx作为HLS边缘,管理两个服务器自然是比一个要费劲。 
若使用后者,即RTMP回源后边缘切片,能节省骨干带宽,只有一路回源,
难点在于访问HLS时要发起 RTMP回源连接。

正因为业务逻辑会是边缘服务器的难点,所以SRS对于上行边缘,采取直接代理方式,
并没有采取 边缘缓存方式。所谓边缘缓存方式,即推流到边缘时边缘也会当作源站直接缓存(作为源站), 
然后转发给源站。边缘缓存方式看起来先进,这个边缘节点不必回源,实际上加大了集群的逻辑难度, 
不如直接作为代理方式简单。

四、RTMP 集群(源/边缘) 架构

SRS集群支持两种模式: forward 和 edge
步骤一:
Any RTMP encoder push RTMP stream to RTMP (origin/edge)server,
    where SRS RTMP Edge server will forward stream to origin.
RTMP编码器推送RTMP流有两个目的地址:
1. RTMP源服务器,
2. SRS RTMP边缘服务器;
   推送到SRS RTMP边缘服务器的RTMP流会被再forward到RTMP源服务器;
+---------+       +-----------------+     +-----------------------+ 
+ Encoder +--+-->-+  SRS(RTMP Edge) +--->-+     (RTMP Origin)     | 
+---------+  |    +-----------------+     |   SRS/FMS/NGINX-RTMP  |
             |                            |    Red5/HELIX/CRTMP   |
             +-------------------------->-+         ......        |
                                          +-----------------------+ 
步骤二:
SRS边缘服务器从RTMP源服务器或上游SRS边缘服务器拉流,
这时,当前的SRS边缘服务器即可以为客户端提供直播播放服务,
也可以做为下一级SRS边缘服务器的源;
+-------------+    +-----------------+      +--------------------+
| RTMP Origin +-->-+  SRS(RTMP Edge) +--+->-+  Client(RTMP/HLS)  |
+-------------+    +-----------------+  |   |  Flash/IOS/Android |
                                        |   +--------------------+
                                        |
                                        |   +-----------------+
                                        +->-+  SRS(RTMP Edge) +
                                            +-----------------+

基于SRS的RTMP分发技术方案相关推荐

  1. Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究

    几年来,我们在做无纸化同屏或在线教育相关场景的时候,总是被一件事情困扰:如何实现Android平台的系统内录,并推送到其他播放端,常用的场景比如做无纸化会议或教育的时候,主讲人或老师需要放一个视频,该 ...

  2. 基于SRS搭建RTMP直播流媒体服务器

    软件定位 SRS 定位是运营级的互联网直播服务器集群,追求更好的概念完整性和最简单实现的代码. 运营级:商业运营追求极高的稳定性.良好的系统对接.错误排查和处理机制.譬如日志文件格式.reload.系 ...

  3. TI基于MSP430F67641的电能表技术方案

    1.三相四线硬件架构 1.1 阻容降压供电 1.2 电压输入 1.3 电流输入 2.RMS有效值计算 3.计量参数算法 3.1 有功和无功 3.2 视在功率 3.3 三相总功率 3.4 电能 3.5 ...

  4. Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨

    背景 随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求.目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务 ...

  5. php流媒体技术srs,基于SRS开源直播系统设计与实现.doc

    基于SRS开源直播系统设计与实现 基于SRS开源直播系统设计与实现 摘 要: 针对校内直播系统存在并发性能低下的问题,提出一种基于SRS开源软件的校内直播解决方案.首先在CentOS的环境下部署Ngi ...

  6. php ajax mysql 分页查询_基于PHP_MySql_Ajax的分页技术方案

    一.引言 Ajax的全称是AsynchronousJavaScriptAndXML(异步JavaScript和XML),它不是一项新技术,而是很多成熟的技术的集合. 和Applet,Flash相比,A ...

  7. android 消息推送方法,一种基于Android系统的消息推送方法技术方案

    [技术实现步骤摘要] 本专利技术涉及一种基于Android系统的消息推送方法,属于计算机 技术介绍 推送功能在手机应用开发中越来越重要,已经成为手机开发的必选项.消息推送,就是在互联网上通过定期传送用 ...

  8. 基于dtmf发送救援信息(gps坐标)传送的技术方案

    基于dtmf发送救援信息(gps坐标)传送的技术方案 项目发起地址:https://github.com/liangdas/dtmfsos 在医疗救护领域,抢救病人最关键的因素是能在最短的时间内到达现 ...

  9. 电子巡更系统服务器,一种基于iBeacon技术的电子巡更系统技术方案

    [技术实现步骤摘要] 一种基于iBeacon技术的电子巡更系统 本专利技术涉及iBeacon ,具体的说,是一种基于iBeacon技术的电子巡更系统. 技术介绍 电子巡更系统是管理人员监督巡更人员是否 ...

最新文章

  1. docker学习系列14 使用haproxy实现mysql集群的负载均衡...
  2. 解决mysql插入中文出现错误Incorrect string value: ‘\xE7\xA8\x8B\xE5\xBA\x8F...‘ for column ‘course‘ at row 1
  3. 系统微服务签发token
  4. android xml中设置水平虚线及竖直虚线
  5. 【Codeforces - 找不到题号】三元环计数(bitset优化,压位)
  6. Tomcat4/5连接池的设置及简单应用示例
  7. 职业高中计算机网络试讲稿,2021教师资格证考试面试高中信息技术试讲稿——《建立数据库的基本过程》...
  8. python二十四点_Python秒算24点,行还是不行?
  9. kafka中topic默认属性_分享:Kafka 的 Lag 计算误区及正确实现
  10. django的web开发笔记1(智能诊断系统数据概览记录)
  11. python 幂运算 整数_在Python中检查一个数字是否是另一个数字的幂
  12. IOS开发UI篇—导航控制器属性和基本使用
  13. P2:图像分类:KNN与线性分类器
  14. 为何有 60% 的程序员拒绝公开讨论薪资?
  15. xcode4.1自带SVN配置
  16. unity, 判断可见性
  17. Android TextToSpeech TTS中文文本转语音(语音合成)
  18. linux系统重装win10系统不用u盘,win10不用u盘怎么重装系统
  19. java移动业务大厅案例_基于Java的SOSO移动大厅项目(功能全部实现了)
  20. TCP connection succeeded but Erlang distribution failed

热门文章

  1. 从程序架构提升安全性
  2. 牛客多校5 - Graph(字典树+分治求最小生成树)
  3. 用于web网页的html文件属于,南开15春学期《Web页面设计》在线作业满分答案
  4. tensorflow包_在Keras和Tensorflow中使用深度卷积网络生成Meme(表情包)文本
  5. 数据分析与挖掘理论-数据探索
  6. 27计算机表演赛命题,27届计算机表演赛命题搜索赛答案(数学3)
  7. docker容器内无法下载到alpine的资源,报错network error (check Internet connection and firewall)
  8. JMP指令转换公式推导
  9. 数据结构与算法 | 二叉树的实现
  10. Python将序列分解为单独的变量