0%

Nginx 中文路径404

工具

SecureFXPortable 文件上传
SecureCRTPortable shell 命令
centos 操作系统

造成原因

使用 SecureFXPortable 上传的中文文件夹在SecureCRTPortable中查看是乱码,导致nginx 无法匹配到相应的目录造成404。

处理方式

  1. 确保操作系统可以输入中文,并在 SecureCRTPortable中可以正确显示。
  2. 将 SecureFXPortable 和 SecureCRTPortable中的编码格式全部设置为utf-8
  3. 在 SecureCRTPortable 安装的文件夹中进入 Data\Settings\Config\Sessions 找到 相对应的 xx.xx.xx.xx.ini 文件,在文件中找到D:"Filenames Always Use UTF8" 将他的值修改为00000001

配置参数说明

1. id

2. path

说明

匹配拦截url

3. serviceId

说明

需要配置 eureka , serviceId 为已注册的某一服务name。

4. url

说明

可以不需要配置 eureka , 直接重定向或者跳转到指定的 url 中,前缀的处理方式参考 stripPrefix

5. stripPrefix

path 匹配是否保留path拦截部分,默认为true。true 不保留,false 保留。

1
2
3
4
5
6
7
8
9
10
zuul:
ignoredServices: '*'
host:
connect-timeout-millis: 20000
socket-timeout-millis: 20000
routes:
cache-service:
path: /cache/**
serviceId: data-cache-service
stripPrefix: false

说明
stripPrefix 为true,请求url http://localhost:8000/cache/index ,会实际跳转到微服务data-cache-service 中的 url为/index 的方法中。
stripPrefix 为false,请求url http://localhost:8000/cache/index ,会实际跳转到微服务data-cache-service 中的 url为/cache/index 的方法中。

6. retryable

7. sensitiveHeaders

8. customSensitiveHeaders

#限流方式

限流的常用处理手段有:计数器、滑动窗口、漏桶、令牌。

限流神器
Guava RateLimiter
Guava RateLimiter 基于令牌桶算法,我们只需要告诉RateLimiter系统限制的QPS是读书,那么RateLimiter将以这个速度往桶里面放令牌,然后请求的时候,通过 tryAcquire() 方法向RateLimiter获取许可(令牌)。

开发过程中常见的安全问题

必要系统参数需要加密

java 开发,一般都使用spring,比如jdbc等系统参数参数我们通常会抽出一个jdbc.properites,大部分情况 jdbc.properties 中的数据都是明文展示的。这样假如我们的服务器被攻破那么数据库相关数据也基本可被攻击者完全查看到。

解决方法

  1. jdbc.properties中的必要数据做加密,不要在jdbc.properties显示重要的明文信息。
  2. spring.xml 中需要引入jdbc.properties 并对相关信息进行解密。这样就无法通过<context:property-placeholder location="classpath*:conf/*.properties" ignore-unresolvable="true"/>通过这种方式来引入相关配置文件。而需要通过配置bean,并在bean对相关数据进行解密。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    spring.xml
    <bean class="com.adups.dbencrypt.EncryptPropertyPlaceholderConfigure" p:locations="classpath*:conf/*.properties" p:ignoreUnresolvablePlaceholders="true">
    </bean>
    EncryptPropertyPlaceholderConfigure.java
    public class EncryptPropertyPlaceholderConfigure extends PropertyPlaceholderConfigurer {
    private String[] encryptPropNames = {"jdbc_url", "jdbc_username","jdbc_password"};
    protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
    throws BeansException {
    try {
    for (String prop : encryptPropNames) {
    String url = props.getProperty(prop);
    if (url != null) {
    props.setProperty(prop, DbEncryptUtils.getDecryptString(url));
    }
    }
    super.processProperties(beanFactoryToProcess, props);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

文件上传限制

文件上传必须对文件格式做限制,防止用户上传脚本木马。针对文件上传后展示最好使用nginx,针对tomcat不能让用户上传jsp文件。假如用户上传jsp文件,并调取上传的这个jsp,如果这个jsp经过tomcat解析,那么用户就可以通过上传jsp脚本木马获取到部署代码,并获得服务器。

java 执行shell 需要对特殊字符做过滤

如果在java 程序中需要调取liunx shell 命令,那么在执行命令之前对用户传入的参数中的特殊字符(” ‘ | )等做限制和替换。

XSS漏洞

java web 一般使用 filter (XSSRequestWrapper)对请求参数进行参数格式化,过滤可能存在xss漏洞的参数。但是这个方法对 form 表单类型为multipart/form-data是没有效果的。所以针对form为multipart/form-data时,需要单独进行XSS处理。

获取IP

我们从网上找java获取ip的代码,会发现很多代码都是从 request 的头中解析ip。但是这种方式很不安全,用户可以手动设置这些值导致ip获取不准确。
使用request.getRemoteAddr();获取实际IP。

服务器权限

针对一个服务器可能需要部署很多应用比如tomcat mysql nginx 等。针对不同的应用我们最好配置不同的用户,并严重控制每种用户的权限。

location正则写法

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
location  = / {
# 精确匹配 / ,主机名后面不能带任何字符串
[ configuration A ]
}

location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
# 但是正则和最长字符串会优先匹配
[ configuration B ]
}

location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration C ]
}

location ~ /documents/Abc {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration CC ]
}

location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
[ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 结尾的请求
# 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
[ configuration E ]
}

location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在
[ configuration F ]
}

location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
# F与G的放置顺序是没有关系的
[ configuration G ]
}

location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
[ configuration H ]
}

location ~* /js/.*/\.js
  • = 开头标识精确匹配
    如 A 中只匹配根目录结尾的请求,后面不能带任何字符串
  • ^~ 开头表示 uri 以某个常规字符串开头,不是正则匹配。满足条件不需继续往下匹配
  • ~ 开头表示区分大小写的正则匹配,满足条件还需继续往下匹配
  • ~* 开头表示不区分大小写的正则匹配,满足条件还需继续往下匹配
  • / 通用匹配,如果没有其他匹配,任何请求都会匹配到
  • 顺序 <> 优先级

    (location=) > (location 完整路径) > (location ^~ 路径) > (location ~, ~* 正则顺序) > (location 部分起始路径) > (/)

上面的匹配结果
按照上面的location写法,以下的匹配示例成立:

  • / -> config A
    精确完全匹配,即使/index.html也匹配不了
  • /downloads/download.html -> config B
    匹配B以后,往下没有任何匹配,采用B
  • /images/1.gif -> configuration D
    匹配到F,往下匹配到D,停止往下
  • /images/abc/def -> config D
    最长匹配到G,往下匹配D,停止往下
    你可以看到 任何以/images/开头的都会匹配到D并停止,FG写在这里是没有任何意义的,H是永远轮不到的,这里只是为了说明匹配顺序
  • /documents/document.html -> config C
    匹配到C,往下没有任何匹配,采用C
  • /documents/1.jpg -> configuration E
    匹配到C,往下正则匹配到E
  • /documents/Abc.jpg -> config CC
    最长匹配到C,往下正则顺序匹配到CC,不会往下到E

实际使用建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
所以实际使用中,个人觉得至少有三个匹配规则定义,如下:
#直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。
#这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
proxy_pass http://tomcat:8080/index
}
# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
root /webroot/res/;
}
#第三个规则就是通用规则,用来转发动态请求到后端应用服务器
#非静态文件请求就默认是动态请求,自己根据实际把握
#毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
proxy_pass http://tomcat:8080/
}

Rewrite规则

rewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://seanlook.com/a/we/index.php?id=1&u=str 只对/a/we/index.php重写。语法rewrite regex replacement [flag];

如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。

表明看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:

  1. 执行server块的rewrite指令
  2. 执行location匹配
  3. 执行选定的location中的rewrite指令

如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。

flag标志位

  • last : 相当于Apache的[L]标记,表示完成rewrite
  • break : 停止执行当前虚拟主机的后续rewrite指令集
  • redirect : 返回302临时重定向,地址栏会显示跳转后的地址
  • permanent : 返回301永久重定向,地址栏会显示跳转后的地址

因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。这里 last 和 break 区别有点难以理解:

  1. last一般写在server和if中,而break一般使用在location中
  2. last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
  3. break和last都能组织继续执行后面的rewrite指令

if指令与全局变量

if判断指令

语法为if(condition){...},对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(conditon)可以是如下任何内容:
- 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
- 直接比较变量和内容时,使用=或!=
- 正则表达式匹配,*不区分大小写的匹配,!~区分大小写的不匹配

-f和!-f用来判断是否存在文件
-d和!-d用来判断是否存在目录
-e和!-e用来判断是否存在文件或目录
-x和!-x用来判断文件是否可执行

例如:

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
30
31
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /msie/$1 break;
} //如果UA包含"MSIE",rewrite请求到/msid/目录下

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
set $id $1;
} //如果cookie匹配正则,设置变量$id等于正则引用部分

if ($request_method = POST) {
return 405;
} //如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302

if ($slow) {
limit_rate 10k;
} //限速,$slow可以通过 set 指令设置

if (!-f $request_filename){
break;
proxy_pass http://127.0.0.1;
} //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查

if ($args ~ post=140){
rewrite ^ http://example.com/ permanent;
} //如果query string中包含"post=140",永久重定向到example.com

location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.jefflei.com www.leizhenfang.com;
if ($invalid_referer) {
return 404;
} //防盗链
}
全局变量

下面是可以用作if判断的全局变量

  • $args : #这个变量等于请求行中的参数,同$query_string
  • $content_length : 请求头中的Content-length字段。
  • $content_type : 请求头中的Content-Type字段。
  • $document_root : 当前请求在root指令中指定的值。
  • $host : 请求主机头字段,否则为服务器名称。
  • $http_user_agent : 客户端agent信息
  • $http_cookie : 客户端cookie信息
  • $limit_rate : 这个变量可以限制连接速率。
  • $request_method : 客户端请求的动作,通常为GET或POST。
  • $remote_addr : 客户端的IP地址。
  • $remote_port : 客户端的端口。
  • $remote_user : 已经经过Auth Basic Module验证的用户名。
  • $request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。
  • $scheme : HTTP方法(如http,https)。
  • $server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
  • $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
  • $server_name : 服务器名称。
  • $server_port : 请求到达服务器的端口号。
  • $request_uri : 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
  • $uri : 不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
  • $document_uri : 与$uri相同。

例:http://localhost:88/test1/test2/test.php
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:/var/www/html
$request_filename:/var/www/html/test1/test2/test.php

常用正则

  • . : 匹配除换行符以外的任意字符
  • ? : 重复0次或1次
  • + : 重复1次或更多次
  • * : 重复0次或更多次
  • \d :匹配数字
  • ^ : 匹配字符串的开始
  • $ : 匹配字符串的介绍
  • {n} : 重复n次
  • {n,} : 重复n次或更多次
  • [c] : 匹配单个字符c
  • [a-z] : 匹配a-z小写字母的任意一个

小括号()之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容。正则里面容易让人困惑的是\转义特殊字符。

rewrite实例

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
30
31
32
http {
# 定义image日志格式
log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;
# 开启重写日志
rewrite_log on;

server {
root /home/www;

location / {
# 重写规则信息
error_log logs/rewrite.log notice;
# 注意这里要用‘’单引号引起来,避免{}
rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;
# 注意不能在上面这条规则后面加上“last”参数,否则下面的set指令不会执行
set $image_file $3;
set $image_type $4;
}

location /data {
# 指定针对图片的日志格式,来分析图片类型和大小
access_log logs/images.log mian;
root /data/images;
# 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
try_files /$arg_file /image404.html;
}
location = /image404.html {
# 图片不存在返回特定的信息
return 404 "image not found\n";
}
}

对形如/images/ef/uh7b3/test.png的请求,重写到/data?file=test.png,于是匹配到location /data,先看/data/images/test.png文件存不存在,如果存在则正常响应,如果不存在则重写tryfiles到新的image404 location,直接返回404状态码。

例2:

1
rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;

对形如/images/bla_500x400.jpg的文件请求,重写到/resizer/bla.jpg?width=500&height=400地址,并会继续尝试匹配location。

Spring Boot 上传文件

依赖jar

pom.xml

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

java

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@RestController
@RequestMapping
public class UploadController {

@RequestMapping(value = "/uploadDelta.do")
@ResponseBody
public Map<String, Object> updateDelta(MultipartFile file) {
Map<String, Object> result = new HashMap<String, Object>();

String rootFilePath = uploadpath;

//文件最后保存的目录
String path_save =
"/" + paramer.getProjectId() + "/" + deltaEntity.getId() + "/";/**文件保存路径(没有文件名称)**/
String saveFilePath = rootFilePath + path_save;
File allPathFile = new File(saveFilePath);
if (!allPathFile.exists()) {
allPathFile.mkdirs();
logger.info("创建文件夹,路径:" + allPathFile);
}

Map<String, String> map = new HashMap<String, String>();

String fileName = paramer.getFile().getOriginalFilename(); //获取文件名
String[] fileNameSplit = fileName.split("\\.");//获取文件拓展名
String compress = fileNameSplit[fileNameSplit.length - 1];

String randomFilename = UUID.randomUUID().toString() + "." + compress;
File destFile = new File(saveFilePath + randomFilename);//随机生成的文件名

logger.info("文件保存路径:" + saveFilePath + randomFilename);

try {//文件写入硬盘
paramer.getFile().transferTo(new File(saveFilePath + randomFilename));
} catch (Exception e) {
e.printStackTrace();
logger.error("文件保存到硬盘失败");
result.put("status", ResultParams.PROJECT_FAIL_STATUS);
result.put("msg", "文件上传失败");
return result;
}


return null;
}
}

大文件上传异常

spring boot 限制一次表单提交总共文件的最大大小为10MB,每一个文件的大小为1MB.

1
2
3
4
5
MultipartProperties

private String maxFileSize = "1Mb";
private String maxRequestSize = "10Mb";

解决方法

  1. 直接通过配置文件解决

    application.properties

    1
    2
    3
    4
    spring.http.multipart.max-request-size=1024MB 
    # 大小设置为1G
    spring.http.multipart.max-file-size=1024MB
    # 大小设置为1G
  2. 通过代码层级修改文件上传大小限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import org.springframework.web.multipart.MultipartResolver;
    import org.springframework.web.multipart.commons.CommonsMultipartResolver;


    @Bean(name = "multipartResolver")
    public MultipartResolver multipartResolver() {
    CommonsMultipartResolver resolver = new CommonsMultipartResolver();
    resolver.setDefaultEncoding("UTF-8");
    resolver.setResolveLazily(true);//resolveLazily属性启用是为了推迟文件解析,以在在UploadAction中捕获文件大小异常
    resolver.setMaxInMemorySize(40960);
    resolver.setMaxUploadSize(500 * 1024 * 1024);//上传文件大小 50M 50*1024*1024
    return resolver;
    }

    在spring boot 1.5.4版本中可以直接通过 @Bean("multipartResolver") 进行注入。

注意点

Hash

摘要

数据结构基本分为

  • 结构体(或对象)
  • 数组
  • 链表

哈希函数

Hash也叫散列,hash操作其本质上就是建给一个数据映射成另一个数据,通常情况下原数据的长度
比hash后的数据容量大。这种映射关系我们称为哈希函数。
通常 哈希函数的输入要远远多于哈希值所表示的总数,就有可能两个不同的输入对应同一个哈希值。
通常把具有不同关键码而具有相同哈希值的记录称为“同义词”。

哈希表

哈希表是一种数据结构,实现key-value的快速存取。

HasshCode

  • 相等的对象必须要有相同的哈希码。
  • 不相等的对象可能会存在相同的哈希码。
  • 有同一个哈希值的对象不一定相等。
    HashCode 不可信,因此不要将 HashCode 作为key。
    使用基于哈希的算法,比如 SHA1 。来降低hashcode的冲突。

HashTable

HashMap

线程不安全的原因

因为在自动调整大小的机制下,如果线程试着去添加或者获取一个对象,Map可能会使用旧的索引值,
这样就不会找到Entry对象所在的新桶。

保证线程安全的HashMap实现:ConcurrentHashMap。对于ConcurrentMap来说,只有桶是同步的,
这样如果多个线程不使用同一个桶或者调整内部数组的大小,它们可以同时调用get(),remove(),
或者put()方法。

Spring Boot RestTemplate 中文乱码

解决方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
messageConverters.addAll(restTemplate.getMessageConverters());
for(HttpMessageConverter<?> converter :messageConverters) {
if(converter instanceof StringHttpMessageConverter) {
converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
break;
}
}
restTemplate.setMessageConverters(messageConverters);
return restTemplate;
}

Spring Cloud Feign HttpClient 中文乱码

Spring Cloud Feign HttpClient

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>

<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.6.0</version>
</dependency>

Spring Cloud Feign HttpClient 传递中文乱码

推荐使用 ok-http

1
2
3
4
5
6
7
8
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>

搭建直播流服务器

技术

  • Nginx
  • nginx-rtmp-module-1.1.11 (nginx 插件)

步骤

  1. 下载nginx 和 nginx-rtmp-module 源文件
  2. 解压 nginx 和 nginx-rtmp-module
  3. ./configure –add-module=/home/shichangcheng/live/nginx-rtmp-module-1.1.11(将nginx第三方模块加入nginx)
  4. make ; make install;
  5. 配置nginx.conf
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
http {
include mime.types;
default_type application/octet-stream;


sendfile on;

keepalive_timeout 65;

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

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

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

# hls living
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}

location /stat.xsl {
root /usr/local/nginx/html/nginx-rtmp-module/;
}

location /control {
rtmp_control all;
}
location /hls {
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /home/shichangcheng/live/my-app;
}
}
}

rtmp {
server {
listen 1935;

application hls {
live on;
hls on;
hls_path /home/shichangcheng/live/my-app/hls;
hls_fragment 5s;
}

application live{
live on;
max_connections 1024;
allow play all;

record_path /home/shichangcheng/live/my-app/live;

recorder audio
{
record audio;
record_suffix -%d-%b-%y-%T.flv;
}
recorder chunked

{
record all;
#record_max_size 5120K;
record_interval 15s;
record_path /home/shichangcheng/live/my-app/live/chunked;
}
}
}
}

遇到的问题

  1. liunx 的权限
  2. nginx user 修改为root
  3. rtmp 中 record_path 保存客户端采集视频信息推动到服务器的地址,需要注意该文件的读写权限。
  4. 防火墙 iptables -A INPUT -m state –state NEW -m tcp -p tcp –dport 1935 -j ACCEPT

推流地址

rtmp://123.206.231.130/hls/(movie)
(movie)为表示生成流文件的名字

播放地址

rtmp地址:rtmp://123.206.231.130/hls/(movie)
hvl 地址:http://123.206.231.138/hls/movie.m3u8

直播协议

  1. rtmp :rtmp 延时较低,需要使用flash,或者解码器才能播放。
  2. hlv :hlv 延时较高,自己测未进过cdn一般优化,延时5。移动端网页播放使用HLV协议。

客户端

  1. 直播工具可以使用金山云的DEMO
  2. 播放器
  • H5 只能使用 hlv 地址
  • 金山云提供的播放SDK
  • PC客户端 VLC Media Player