mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-22 19:00:01 +08:00
http服务器支持mmap,提示性能
This commit is contained in:
parent
2bc918cd2e
commit
144fb20339
120
src/Http/FileReader.cpp
Normal file
120
src/Http/FileReader.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FileReader.h"
|
||||||
|
#include "Util/util.h"
|
||||||
|
#include "Util/uv_errno.h"
|
||||||
|
#include "Util/logger.h"
|
||||||
|
#include "Common/config.h"
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define ENABLE_MMAP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FileReader::FileReader(const std::shared_ptr<FILE> &fp, uint64_t offset, uint64_t max_size) {
|
||||||
|
_fp = fp;
|
||||||
|
_max_size = max_size;
|
||||||
|
#ifdef ENABLE_MMAP
|
||||||
|
do {
|
||||||
|
int fd = fileno(fp.get());
|
||||||
|
if (fd < 0) {
|
||||||
|
WarnL << "fileno失败:" << get_uv_errmsg(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto ptr = (char *) mmap(NULL, max_size, PROT_READ, MAP_SHARED, fd, offset);
|
||||||
|
if (!ptr) {
|
||||||
|
WarnL << "mmap失败:" << get_uv_errmsg(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_map_addr.reset(ptr,[max_size](char *ptr){
|
||||||
|
munmap(ptr,max_size);
|
||||||
|
});
|
||||||
|
} while (false);
|
||||||
|
#endif
|
||||||
|
if(!_map_addr){
|
||||||
|
//未映射
|
||||||
|
fseek(fp.get(), offset, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FileReader::~FileReader() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class BufferMmap : public Buffer{
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<BufferMmap> Ptr;
|
||||||
|
BufferMmap(const std::shared_ptr<char> &map_addr,uint64_t offset,int size){
|
||||||
|
_map_addr = map_addr;
|
||||||
|
_data = map_addr.get() + offset;
|
||||||
|
_size = size;
|
||||||
|
};
|
||||||
|
virtual ~BufferMmap(){};
|
||||||
|
//返回数据长度
|
||||||
|
char *data() const override {
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
uint32_t size() const override{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<char> _map_addr;
|
||||||
|
char *_data;
|
||||||
|
uint32_t _size;
|
||||||
|
};
|
||||||
|
Buffer::Ptr FileReader::read(int size) {
|
||||||
|
if(_read_offset >= _max_size){
|
||||||
|
//文件读完了
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int iReq = MIN(size,_max_size - _read_offset);
|
||||||
|
|
||||||
|
if(!_map_addr){
|
||||||
|
//映射失败,fread模式
|
||||||
|
int iRead;
|
||||||
|
auto ret = std::make_shared<BufferRaw>(iReq + 1);
|
||||||
|
do{
|
||||||
|
iRead = fread(ret->data(), 1, iReq, _fp.get());
|
||||||
|
}while(-1 == iRead && UV_EINTR == get_uv_error(false));
|
||||||
|
|
||||||
|
if(iRead > 0){
|
||||||
|
//读到数据了
|
||||||
|
ret->setSize(iRead);
|
||||||
|
_read_offset += iRead;
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
||||||
|
//没有剩余数据
|
||||||
|
_read_offset = _max_size;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ret = std::make_shared<BufferMmap>(_map_addr,_read_offset,iReq);
|
||||||
|
_read_offset += iReq;
|
||||||
|
return std::move(ret);
|
||||||
|
}
|
50
src/Http/FileReader.h
Normal file
50
src/Http/FileReader.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZLMEDIAKIT_FILEREADER_H
|
||||||
|
#define ZLMEDIAKIT_FILEREADER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <memory>
|
||||||
|
#include "Network/Buffer.h"
|
||||||
|
using namespace std;
|
||||||
|
using namespace toolkit;
|
||||||
|
|
||||||
|
class FileReader {
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<FileReader> Ptr;
|
||||||
|
FileReader(const std::shared_ptr<FILE> &fp,uint64_t offset,uint64_t max_size);
|
||||||
|
~FileReader();
|
||||||
|
Buffer::Ptr read(int size);
|
||||||
|
private:
|
||||||
|
std::shared_ptr<FILE> _fp;
|
||||||
|
uint64_t _max_size;
|
||||||
|
uint64_t _read_offset = 0;
|
||||||
|
std::shared_ptr<char> _map_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //ZLMEDIAKIT_FILEREADER_H
|
@ -44,6 +44,7 @@
|
|||||||
#include "Util/base64.h"
|
#include "Util/base64.h"
|
||||||
#include "Util/SHA1.h"
|
#include "Util/SHA1.h"
|
||||||
#include "Rtmp/utils.h"
|
#include "Rtmp/utils.h"
|
||||||
|
#include "FileReader.h"
|
||||||
using namespace toolkit;
|
using namespace toolkit;
|
||||||
|
|
||||||
namespace mediakit {
|
namespace mediakit {
|
||||||
@ -601,7 +602,6 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||||||
} else {
|
} else {
|
||||||
//分节下载
|
//分节下载
|
||||||
pcHttpResult = "206 Partial Content";
|
pcHttpResult = "206 Partial Content";
|
||||||
fseek(pFilePtr.get(), iRangeStart, SEEK_SET);
|
|
||||||
//分节下载返回Content-Range头
|
//分节下载返回Content-Range头
|
||||||
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
|
httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl);
|
||||||
}
|
}
|
||||||
@ -617,51 +617,34 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||||||
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file");
|
throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file");
|
||||||
}
|
}
|
||||||
//回复Content部分
|
//回复Content部分
|
||||||
std::shared_ptr<int64_t> piLeft(new int64_t(iRangeEnd - iRangeStart + 1));
|
|
||||||
|
|
||||||
GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize);
|
GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize);
|
||||||
|
FileReader::Ptr fileReader = std::make_shared<FileReader>(pFilePtr,iRangeStart,iRangeEnd - iRangeStart + 1);
|
||||||
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
weak_ptr<HttpSession> weakSelf = dynamic_pointer_cast<HttpSession>(shared_from_this());
|
||||||
auto onFlush = [pFilePtr,bClose,weakSelf,piLeft]() {
|
|
||||||
TimeTicker();
|
auto onFlush = [fileReader,bClose,weakSelf]() {
|
||||||
auto strongSelf = weakSelf.lock();
|
auto strongSelf = weakSelf.lock();
|
||||||
while(*piLeft && strongSelf){
|
if(!strongSelf){
|
||||||
|
//本对象已经销毁
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while(true){
|
||||||
//更新超时计时器
|
//更新超时计时器
|
||||||
strongSelf->_ticker.resetTime();
|
strongSelf->_ticker.resetTime();
|
||||||
//从循环池获取一个内存片
|
//读取文件
|
||||||
auto sendBuf = strongSelf->obtainBuffer();
|
auto sendBuf = fileReader->read(sendBufSize);
|
||||||
sendBuf->setCapacity(sendBufSize);
|
if (!sendBuf) {
|
||||||
//本次需要读取文件字节数
|
|
||||||
int64_t iReq = MIN(sendBufSize,*piLeft);
|
|
||||||
//读文件
|
|
||||||
int iRead;
|
|
||||||
do{
|
|
||||||
iRead = fread(sendBuf->data(), 1, iReq, pFilePtr.get());
|
|
||||||
}while(-1 == iRead && UV_EINTR == get_uv_error(false));
|
|
||||||
//文件剩余字节数
|
|
||||||
*piLeft -= iRead;
|
|
||||||
|
|
||||||
if (iRead < iReq || !*piLeft) {
|
|
||||||
//文件读完
|
//文件读完
|
||||||
if(iRead > 0) {
|
|
||||||
sendBuf->setSize(iRead);
|
|
||||||
strongSelf->send(sendBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strongSelf->isSocketBusy()){
|
if(strongSelf->isSocketBusy()){
|
||||||
//套接字忙,我们等待触发下一次onFlush事件
|
//套接字忙,我们等待触发下一次onFlush事件
|
||||||
//待所有数据flush到socket fd再移除onFlush事件监听
|
//待所有数据flush到socket fd再移除onFlush事件监听
|
||||||
//标记文件读写完毕
|
//标记文件读写完毕
|
||||||
*piLeft = 0;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//文件全部flush到socket fd,可以直接关闭socket了
|
//文件全部flush到socket fd,可以直接关闭socket了
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//文件还未读完
|
//文件还未读完
|
||||||
sendBuf->setSize(iRead);
|
|
||||||
if(strongSelf->send(sendBuf) == -1) {
|
if(strongSelf->send(sendBuf) == -1) {
|
||||||
//socket已经销毁,不再监听onFlush事件
|
//socket已经销毁,不再监听onFlush事件
|
||||||
return false;
|
return false;
|
||||||
@ -673,7 +656,7 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) {
|
|||||||
//socket还可写,继续写socket
|
//socket还可写,继续写socket
|
||||||
}
|
}
|
||||||
|
|
||||||
if(bClose && strongSelf) {
|
if(bClose) {
|
||||||
//最后一次flush事件,文件也发送完毕了,可以关闭socket了
|
//最后一次flush事件,文件也发送完毕了,可以关闭socket了
|
||||||
strongSelf->shutdown(SockException(Err_shutdown,"read file eof"));
|
strongSelf->shutdown(SockException(Err_shutdown,"read file eof"));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user