bootstrap外不引用连接_网络编程Netty IoT百万长连接优化,万字长文精讲

IoT是什么

The Internet of things的简称IoT,即是物联网的意思

IoT推送系统的设计

比如说,像一些智能设备,需要通过APP或者微信中的小程序等,给设备发送一条指令,让这个设备下载或者播放音乐,那么需要做什么才可以完成上面的任务呢?

e1350e298f476f4823635ae027cedbdc.png

首先需要推送服务器,这个服务器主要负责消息的分发,不处理业务消息;设备会连接到推送服务器,APP通过把指令发送到推送服务器,然后推送服务器再把指令分发给相应的设备。

可是,当买设备的人越来越多,推送服务器所能承受的压力就越大,这个时候就需要对推送服务器做集群,一台不行,就搞十台,那么还有一个问题,就是推送服务器增加了,设备如何找到相应的服务器,然后和服务器建立连接呢,注册中心可以解决这个问题,每一台服务器都注册到注册中心上,设备会请求注册中心,得到推送服务器的地址,然后再和服务器建立连接。

而且还会有相应的redis集群,用来记录设备订阅的主题以及设备的信息;APP发送指令到设备,其实就是发送了一串数据,相应的会提供推送API,提供一些接口,通过接口把数据发送过去;而推送API不是直接去连接推送服务器的,中间还会有MQ集群,主要用来消息的存储,推送API推送消息到MQ,推送服务器从MQ中订阅消息,以上就是简单的IoT推送系统的设计。

下面看下结构图:

296da16e8e3af704a142f9d49852b3c8.png

注意:设备连接到注册中心的是短连接,设备和推送服务器建立的连接是长连接

心跳检测机制

简述心跳检测

心跳检测,就是判断对方是否还存活,一般采用定时的发送一些简单的包,如果在指定的时间段内没有收到对方的回应,则判断对方已经挂掉

Netty提供了IdleStateHandler类来实现心跳,简单的使用如下:

pipeline.addFirst(new IdleStateHandler(0, 0, 1, TimeUnit.SECONDS));

下面是IdleStateHandler的构造函数:

public IdleStateHandler(            long readerIdleTime, long writerIdleTime, long allIdleTime,            TimeUnit unit) {        this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);}

四个参数说明:

  1. readerIdleTime,读超时时间
  2. writerIdleTime,写超时时间
  3. allIdleTime,所有事件超时时间
  4. TimeUnit unit,超时时间单位
1b8f5f9349610daff05e2fd6d2b7662c.png

心跳检测机制代码示例

简单示例: 服务端:

static final int BEGIN_PORT = 8088;    static final int N_PORT = 100;    public static void main(String[] args) {        new PingServer().start(BEGIN_PORT, N_PORT);    }    public void start(int beginPort, int nPort) {        System.out.println("启动服务....");        EventLoopGroup bossGroup = new NioEventLoopGroup(1);        EventLoopGroup workerGroup = new NioEventLoopGroup();        ServerBootstrap bootstrap = new ServerBootstrap();        bootstrap.handler(new LoggingHandler(LogLevel.INFO));        bootstrap.group(bossGroup, workerGroup);        bootstrap.channel(NioServerSocketChannel.class);        bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);        bootstrap.childHandler(new ChannelInitializer() {            @Override            protected void initChannel(SocketChannel ch) throws Exception {                ChannelPipeline pipeline = ch.pipeline();                pipeline.addFirst(new IdleStateHandler(0, 0, 1, TimeUnit.SECONDS));                pipeline.addLast(new PingHandler());                //每个连接都有个ConnectionCountHandler对连接记数进行增加                pipeline.addLast(new ConnectionCountHandler());            }        });        bootstrap.bind(beginPort).addListener((ChannelFutureListener) future -> {            System.out.println("端口绑定成功: " + beginPort);        });        System.out.println("服务已启动!");}
public class PingHandler extends SimpleUserEventChannelHandler {    private static final ByteBuf PING_BUF = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer("ping".getBytes()));    private int count;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        ByteBuf buf = (ByteBuf) msg;        byte[] data = new byte[buf.readableBytes()];        buf.readBytes(data);        String str = new String(data);        if ("pong".equals(str)) {            System.out.println(ctx + " ---- " + str);            count--;        }        ctx.fireChannelRead(msg);    }    @Override    protected void eventReceived(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {        if (evt.state() == ALL_IDLE) {            if (count >= 3) {                System.out.println("检测到客户端连接无响应,断开连接:" + ctx.channel());                ctx.close();                return;            }            count++;            System.out.println(ctx.channel() + " ---- ping");            ctx.writeAndFlush(PING_BUF.duplicate());        }        ctx.fireUserEventTriggered(evt);    }}

客户端:

//服务端的IP private static final String SERVER_HOST = "localhost"; static final int BEGIN_PORT = 8088; static final int N_PORT = 100; public static void main(String[] args) {     new PoneClient().start(BEGIN_PORT, N_PORT); } public void start(final int beginPort, int nPort) {     System.out.println("客户端启动....");     EventLoopGroup eventLoopGroup = new NioEventLoopGroup();     final Bootstrap bootstrap = new Bootstrap();     bootstrap.group(eventLoopGroup);     bootstrap.channel(NioSocketChannel.class);     bootstrap.option(ChannelOption.SO_REUSEADDR, true);     bootstrap.handler(new ChannelInitializer() {         @Override         protected void initChannel(SocketChannel ch) {             ch.pipeline().addLast(new PongHandler());         }     });     int index = 0;     int port;     String serverHost = System.getProperty("server.host", SERVER_HOST);     ChannelFuture channelFuture = bootstrap.connect(serverHost, beginPort);     channelFuture.addListener((ChannelFutureListener) future -> {         if (!future.isSuccess()) {             System.out.println("连接失败,退出!");             System.exit(0);         }     });     try {         channelFuture.get();     } catch (ExecutionException e) {         e.printStackTrace();     } catch (InterruptedException e) {         e.printStackTrace();     } }
public class PongHandler extends SimpleChannelInboundHandler {    private static final ByteBuf PONG_BUF = Unpooled.unreleasableBuffer(Unpooled.wrappedBuffer("pong".getBytes()));    @Override    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {        ByteBuf buf = (ByteBuf) msg;        byte[] data = new byte[buf.readableBytes()];        buf.readBytes(data);        String str = new String(data);        if ("ping".equals(str)) {            ctx.writeAndFlush(PONG_BUF.duplicate());        }    }}

服务端输出结果:

0c20ad889d3a7cacb804ecc6a86b9885.png

百万长连接优化

连接优化代码示例

服务端:

 static final int BEGIN_PORT = 11000;    static final int N_PORT = 100;    public static void main(String[] args) {        new Server().start(BEGIN_PORT, N_PORT);    }    public void start(int beginPort, int nPort) {        System.out.println("启动服务....");        EventLoopGroup bossGroup = new NioEventLoopGroup(1);        EventLoopGroup workerGroup = new NioEventLoopGroup();        ServerBootstrap bootstrap = new ServerBootstrap();        bootstrap.group(bossGroup, workerGroup);        bootstrap.channel(NioServerSocketChannel.class);        bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);        bootstrap.childHandler(new ChannelInitializer() {            @Override            protected void initChannel(SocketChannel ch) throws Exception {                ChannelPipeline pipeline = ch.pipeline();                //每个连接都有个ConnectionCountHandler对连接记数进行增加                pipeline.addLast(new ConnectionCountHandler());            }        });        //这里开启 10000到100099这100个端口        for (int i = 0; i < nPort; i++) {            int port = beginPort + i;            bootstrap.bind(port).addListener((ChannelFutureListener) future -> {                System.out.println("端口绑定成功: " + port);            });        }        System.out.println("服务已启动!");    }

客户端:

//服务端的IP    private static final String SERVER_HOST = "192.168.231.129";    static final int BEGIN_PORT = 11000;    static final int N_PORT = 100;    public static void main(String[] args) {        new Client().start(BEGIN_PORT, N_PORT);    }    public void start(final int beginPort, int nPort) {        System.out.println("客户端启动....");        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();        final Bootstrap bootstrap = new Bootstrap();        bootstrap.group(eventLoopGroup);        bootstrap.channel(NioSocketChannel.class);        bootstrap.option(ChannelOption.SO_REUSEADDR, true);        int index = 0;        int port;        String serverHost = System.getProperty("server.host", SERVER_HOST);        //从10000的端口开始,按端口递增的方式进行连接        while (!Thread.interrupted()) {            port = beginPort + index;            try {                ChannelFuture channelFuture = bootstrap.connect(serverHost, port);                channelFuture.addListener((ChannelFutureListener) future -> {                    if (!future.isSuccess()) {                        System.out.println("连接失败,退出!");                        System.exit(0);                    }                });                channelFuture.get();            } catch (Exception e) {            }            if (++index == nPort) {                index = 0;            }        }    }

ConnectionCountHandler类:

public class ConnectionCountHandler extends ChannelInboundHandlerAdapter {    //这里用来对连接数进行记数,每两秒输出到控制台    private static final AtomicInteger nConnection = new AtomicInteger();    static {        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {            System.out.println("连接数: " + nConnection.get());        }, 0, 2, TimeUnit.SECONDS);    }    @Override    public void channelActive(ChannelHandlerContext ctx) {        nConnection.incrementAndGet();    }    @Override    public void channelInactive(ChannelHandlerContext ctx) {        nConnection.decrementAndGet();    }}

上述的代码会打包成jar放到linux上运行,对于上述的优化来说,程序方面的就暂时不做,下面会从操作系统层面进行优化,让其支撑起百万连接。

TCP连接四元组

在优化之前先来看下网络里的一个小知识,TCP连接四元组: 服务器的IP+服务器的POST+客户端的IP+客户端的POST

端口的范围一般是1到65535:

530b573e6194da4b90e27156a02404be.png

配置优化

现在在虚拟机上安装两个linux系统,配置分别是:

地址CPU内存JDK作用192.168.15.130VM-4核8G1.8客户端192.168.15.128VM-4核8G1.8服务端

启动服务端: java -Xmx4g -Xms4g -cp network-study-1.0-SNAPSHOT-jar-with-dependencies.jar com.dongnaoedu.network.netty.million.Server > out.log 2>&1 & 启动客户端: java -Xmx4g -Xms4g -Dserver.host=192.168.15.128 -cp network-study-1.0-SNAPSHOT-jar-with-dependencies.jar com.dongnaoedu.network.netty.million.Client

启动服务端后可以使用tail -f命令查看out.log中的日志:

e1aeeea486b2b3eedbb3d28315599410.png

客户端启动后,如果报了以下错误,需要修改系统的文件最大句柄和进程的文件最大句柄:

Caused by: java.io.IOException: Too many open files        at sun.nio.ch.FileDispatcherImpl.init(Native Method)        at sun.nio.ch.FileDispatcherImpl.(FileDispatcherImpl.java:35)        ... 8 more

优化系统最大句柄: 查看操作系统最大文件句柄数,执行命令cat /proc/sys/fs/file-max,查看最大句柄数是否满足需要,如果不满足,通过vim /etc/sysctl.conf命令插入如下配置:

fs.file-max = 1000000
  1. 设置单进程打开的文件最大句柄数,执行命令ulimit -a查看当前设置是否满足要求:
[root@test-server2 download]# ulimit -a | grep "open files"open files                      (-n) 1024

当并发接入的Tcp连接数超过上限时,就会提示“Too many open files”,所有的新客户端接入将会失败。通过vim /etc/security/limits.conf 修改配置参数:

* soft nofile 1000000* hard nofile 1000000

修改配置参数后注销生效。

  • 如果程序被中断,或报了异常
java.io.IOException: 设备上没有空间    at sun.nio.ch.EPollArrayWrapper.epollCtl(Native Method)    at sun.nio.ch.EPollArrayWrapper.updateRegistrations(EPollArrayWrapper.java:299)    at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:268)    at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:93)    at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86)    at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:105)    at io.netty.channel.nio.SelectedSelectionKeySetSelector.selectNow(SelectedSelectionKeySetSelector.java:56)    at io.netty.channel.nio.NioEventLoop.selectNow(NioEventLoop.java:750)    at io.netty.channel.nio.NioEventLoop$1.get(NioEventLoop.java:71)    at io.netty.channel.DefaultSelectStrategy.calculateStrategy(DefaultSelectStrategy.java:30)    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:426)    at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)    at java.lang.Thread.run(Thread.java:748)
  • 此时可以查看操作系统的日志more /var/log/messages,或在程序启动时执行tail -f /var/log/messages 监控日志。如果日志中出现以下内容,说明需要优化TCP/IP参数
Jun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned socketsJun  4 16:55:01 localserver kernel: TCP: too many orphaned sockets

==优化TCP/IP相关参数:==

  • 查看客户端端口范围限制
cat /proc/sys/net/ipv4/ip_local_port_range
  • 通过vim /etc/sysctl.conf 修改网络参数
  • 客户端修改端口范围的限制
net.ipv4.ip_local_port_range = 1024 65535
  • 优化TCP参数
net.ipv4.tcp_mem = 786432 2097152 3145728net.ipv4.tcp_wmem = 4096 4096 16777216net.ipv4.tcp_rmem = 4096 4096 16777216net.ipv4.tcp_keepalive_time = 1800net.ipv4.tcp_keepalive_intvl = 20net.ipv4.tcp_keepalive_probes = 5net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_fin_timeout = 30

==参数说明:==

net.ipv4.tcp_mem: 分配给tcp连接的内存,单位是page(1个Page通常是4KB,可以通过getconf PAGESIZE命令查看),三个值分别是最小、默认、和最大。比如以上配置中的最大是3145728,那分配给tcp的最大内存=31457284 / 1024 / 1024 = 12GB。一个TCP连接大约占7.5KB,粗略可以算出百万连接≈7.51000000/4=1875000 3145728足以满足测试所需。

net.ipv4.tcp_wmem: 为每个TCP连接分配的写缓冲区内存大小,单位是字节。三个值分别是最小、默认、和最大。

net.ipv4.tcp_rmem: 为每个TCP连接分配的读缓冲区内存大小,单位是字节。三个值分别是最小、默认、和最大。

net.ipv4.tcp_keepalive_time: 最近一次数据包发送与第一次keep alive探测消息发送的事件间隔,用于确认TCP连接是否有效。

net.ipv4.tcp_keepalive_intvl: 在未获得探测消息响应时,发送探测消息的时间间隔。

net.ipv4.tcp_keepalive_probes: 判断TCP连接失效连续发送的探测消息个数,达到之后判定连接失效。

net.ipv4.tcp_tw_reuse: 是否允许将TIME_WAIT Socket 重新用于新的TCP连接,默认为0,表示关闭。

net.ipv4.tcp_tw_recycle: 是否开启TIME_WAIT Socket 的快速回收功能,默认为0,表示关闭。

net.ipv4.tcp_fin_timeout: 套接字自身关闭时保持在FIN_WAIT_2 状态的时间。默认为60。

转载于:https://juejin.im/post/6861560765200105486

作者:狐言不胡言

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/551528.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机基础知识与程序设计二,计算机基础与程序设计.doc

计算机基础与程序设计.doc (17页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;14.9 积分&#xfeff;《计算机基础与稈序设计》是高等教冇H学考试工科备专业的基础课。这门课也是大部分学 生学习计算…

为什么python 为什么没有接口_python没有接口吗

接口只是定义了一些方法&#xff0c;而没有去实现&#xff0c;多用于程序设计时&#xff0c;只是设计需要有什么样的功能&#xff0c;但是并没有实现任何功能&#xff0c;这些功能需要被另一个类&#xff08;B&#xff09;继承后&#xff0c;由 类B去实现其中的某个功能或全部功…

画直线_在鸡面前画一条直线,为什么它会晕?西瓜视频这知识好冷告诉答案

为什么世界有那么多的未解之谜&#xff0c;我们无从而知&#xff0c;今天我们来探讨一下在鸡面前画条直线为什么会晕&#xff1f;你们知道吗&#xff1f;今天西瓜视频这知识好冷告诉你们答案&#xff0c;帮助你们掌握生活中所不知道的涨知识&#xff0c;增加我们的知识库。优秀…

永洪bi_案例分享!永洪BI助力知名三甲医院数字化转型升级

案例一&#xff1a;“新数据需求立刻看到结果”建院至今已有100余年的历史&#xff0c;现已发展成为集医疗、科研、教学为一体的某家三级甲等综合医院&#xff0c;通过永洪科技大数据平台&#xff0c;基于医院的HIS系统为数据源&#xff0c;分别从运营管理、药品管理、病例管理…

github mac 添加 ssh_计算机专业MAC操作技巧(二)

1、MAC 终端启动jupyter jupyter安装与配置就不赘述了&#xff0c;MAC终端启动jupyter有点独特。尝试了很多次都没有打开浏览器&#xff0c;把踩的坑总结一下&#xff1a;一直出现找不到浏览器的错误&#xff0c;在本地浏览器中一直打不开。&#xff08;1&#xff09;、首先先在…

计算机有必要报英语四级吗,我已工作了,现在有必要去考英语四级吗?还是 – 手机爱问...

2010-02-20有哪些是衡量好坏的重要指标呢&#xff1f;眼看就是春节&#xff0c;电视还没买回家&#xff0c;不是偷懒&#xff0c;是不晓得该如何抉择是好&#xff1f;需要网友帮忙。液晶显示不像PDP、CRT那样属于自发光显示。液晶面板也好&#xff0c;背光技术也罢。只是显示屏…

python分支结构说课_Python_3.8平台上的分支结构(模块.类.函数)_11

计算机 python语言_3.8平台上的分支结构(模块.类.函数)11上节说了&#xff0c;python程序有注释、缩进和程序主题。其应用软件由模块--文件*.py分割保存。模块中有变量、函数、类(数据与函数)等。模块是最基础的最小的结构要素单元。并用__main__模块演示了按照较规范的执行顺序…

计算机应用基础自主学习,《计算机应用基础》自主学习指导

本资料可供职业中学、高职及初学计算机基础的学生参考也可以供授课教师参考《计算机应用基础》自主学习指导一、课程内容、要求、目的1、本课程是一门有关计算机知识的入门课程&#xff0c;主要着重计算机的基础知识、基本概念和基本操作技能的学习和培养&#xff0c;并兼顾实用…

java run里面定义变量_Java程序员50多道最热门的多线程和并发面试题(答案解析)...

下面是Java程序员相关的热门面试题&#xff0c;你可以用它来好好准备面试。1) 什么是线程&#xff1f;线程是操作系统能够进行运算调度的最小单位&#xff0c;它被包含在进程之中&#xff0c;是进程中的实际运作单位。程序员可以通过它进行多处理器编程&#xff0c;你可以使用多…

qpython获取手机gps_基于Python获取照片的GPS位置信息

这篇文章主要介绍了基于Python获取照片的GPS位置信息,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 昨天听人说&#xff0c;用手机拍照会带着GPS信息&#xff0c;原来没注意过这个&#xff0c;因此查看下并使用…

计算机盐城工学院和常熟理工,【选专业】这6所二本院校的专业,就业不输一本学生!...

原标题&#xff1a;【选专业】这6所二本院校的专业&#xff0c;就业不输一本学生&#xff01;距离高考只剩下十来天&#xff0c;家长们除了要关心孩子的情况&#xff0c;对于志愿填报也渐渐开始关注起来&#xff0c;这个时候许多家长才发现&#xff0c;志愿填报居然这么麻烦&am…

蒸汽机器人q和锤石q_英雄联盟:圣杯锤石的启发,辅助的作用是否应该更倾向多元化?...

圣杯锤石的套路我个人最早是从主播青蛙那儿了解的&#xff0c;偶然地刷到和锤石有关的剪辑&#xff0c;发现他已经在使用圣杯替代基克的聚合&#xff0c;在多次观察锤石在团战中的表现和圣杯的作用后&#xff0c;我觉得这种打法值得说道说道。当然强不强我无从得知&#xff0c;…

计算机英语的语言特点及教学,计算机英语的语言特点及教学.doc

计算机英语的语言特点及教学.doc (6页)本资源提供全文预览&#xff0c;点击全文预览即可全文预览,如果喜欢文档就下载吧&#xff0c;查找使用更方便哦&#xff01;9.90 积分&#xfeff;计算机英语的语言特点及教学  1计算机英语的语言特点  计算机英语具有简明性  较传统…

npz文件转为npy_Numpy_快速操作数组 4.4 数组的文件输入输出

Numpy作者&#xff1a;PureFFFmennory对象类型&#xff1a;ndarry上一节&#xff1a;4.3 使用向量计算代替数组PureFFFmennory&#xff1a;《Python for Data Analysis 2nd》学习笔记Chapter 4-4.3​zhuanlan.zhihu.com4.4 数组的文件输入与输出NumPy能够以文本或二进制格式保存…

csv导入mysql_mysql导入超大csv指南

mysql导入超大csv指南需求描述手头下载了一个比较大(400Mb)的语料数据&#xff0c;需要从里面提取出某两种语言的句子对&#xff0c;因为数据特别大&#xff0c;且csv并非标准以逗号分隔而是以tab分隔&#xff0c;尝试用Navicat的导入向导导入失败。另外以后也可能会有处理超大…

光滑噪声数据常用的方法_数据挖掘中常用的数据清洗方法

是新朋友吗&#xff1f;记得先点蓝字关注我哦&#xff5e;数据挖掘中常用的数据清洗方法在数据挖掘过程中&#xff0c;数据清洗主要根据探索性分析后得到的一些结论入手&#xff0c;然后主要对四类异常数据进行处理&#xff0c;分别是缺失值(missing value)&#xff0c;异常值(…

华为nova3游戏帧数测试软件,华为nova3最全游戏体验报告:手游玩家一定不能错过...

华为nova3搭载麒麟970人工智能芯片&#xff0c;并支持GPU Turbo、4D智能震撼、游戏免打扰等专为提升游戏体验的技术&#xff0c;那Nova3的实际游戏体验到底如何呢&#xff1f;楼主选择了三款游戏进行测试&#xff0c;分别为王者荣耀、绝地求生 刺激战场和QQ飞车&#xff01;先来…

3 上传分段_32式太极拳教材分段教学:【3】32式太极拳背向演示 .3/25.

观看视频前&#xff0c;请先点击上面的蓝色字“杨式太极拳习练之家教学视频"再点击“关注”&#xff0c;这样您就可以继续免费收到太极拳剑相关教学视频了。加关注后&#xff0c;再【点击】上面的蓝色字“杨式太极拳习练之家教学视频"&#xff0c;进入到“杨式太极拳…

python运用在哪些地方_必看 | 2020年,Python十大应用领域介绍!

原标题&#xff1a;必看 | 2020年&#xff0c;Python十大应用领域介绍&#xff01; python作为一门当下极其火爆的编程语言&#xff0c;得到世界范围内无数编程爱好者和开发者喜欢并不是偶然的&#xff0c;除了要比其他编程语言更容易入门&#xff0c;python还拥有无比广阔的应…

ffmpeg libx264_nginx+ffmpeg搭建流媒体服务器(直播流)

这里实现了简单nginxffmpeg 推本地mp4视频文件的功能&#xff0c;以后将会继续更新环境系统环境&#xff1a;CentOS release 6.7 (Final)需求利用nginx和ffmpeg搭建流媒体服务器利用nginx和ffmpeg搭建流媒体服务器(直播流)&#xff0c;其他流后续会有所更新关于用Nginx搭建flv,…