东风草堂博客

公众号:开发者来风

/proc/sys/net/ipv4/tcp_mem
确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的BDP可以增大这些值(注意,其单位是内存页而不是字节)。

tcp_mem(3个INTEGER变量):low, pressure, high

  • low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。理想情况下,这个值应与指定给tcp_wmem的第2个值相匹配,这第2个值表明,最大页面大小乘以最大并发请求数除以页大小,如131072*300/4096。
  • pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。理想情况下这个值应该是TCP可以使用的总缓冲区大小的最大值,如204800*300/4096。
  • high:允许所有tcp sockets用于排队缓冲数据报的页面量。如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守(512000*300/4096)的原因了。 在这种情况下,提供的价值很大,它能处理很多连接,是所预期的2.5倍;或者使现有连接能够传输2.5倍的数据。 我的网络里为192000 300000 732000
1
2
3
4
5
6
7
8
9
10
11
12
static void tcp_init_mem(void)
{
/* nr_free_buffer_pages()计算ZONE_DMA和ZONE_NORMAL的页数,
* 对于64位系统来说,其实就是所有内存了。
*/
unsigned long limit = nr_free_buffer_pages() / 8;
limit = max(limit, 128UL); /* 不能低于128页 */

sysctl_tcp_mem[0] = limit / 4 * 3; /* 最小值设为3/32的系统内存 */
sysctl_tcp_mem[1] = limit; /* 压力值设为1/8的系统内存 */
sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2; /* 最大值设为3/16的系统内存 */
}

sndbuf是根据什么定的?业务
在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send()、recv()不断的循环收发。

阅读全文 »

深入解析UPnP协议的工作原理与实践应用,包括SSDP发现机制、SOAP控制协议、端口映射配置等核心技术。详细介绍WANIPConnection和WANIPv6FirewallControl服务的使用方法,以及如何利用miniupnp库实现NAT穿透,为P2P应用和远程访问提供完整的解决方案。

阅读全文 »

http1.x的缺点

安全不足和性能不高。
HTTP/2 借用了哈夫曼编码对于Header进行高效压缩,提高传输效率。
HTTP1.X中最大的问题是队头阻塞,HTTP1.X中浏览器对于同一域名的并发连接访问此时是有限的,所以常常会导致只有个位数的连接可以正常工作,后续的连接都会被阻塞。
HTTP1.1支持请求管道化(pipelining)。基于HTTP1.1的长连接,使得请求管线化成为可能。管线化使得请求能够“并行”传输。
注意:这里的“并行”并不是真正意义上的并行传输,需要注意的是,服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容。
也就是说,HTTP管道化可以让我们把先进先出队列从客户端(请求队列)迁移到服务端(响应队列)。
HTTP/2 解决队头阻塞是以 HTTP1.X 管道化的为基础拓展,它使用了二进制流和帧概念解决应用层队头阻塞。应用层的阻塞被解决便是实现流并发传输。
为了控制资源的资源的获取顺序,HTTP在并发传输的基础上实现请求优先级以及流量控制,流的流量控制是考虑接收方是否具备接收能力。

二进制帧

二进制帧保留Header+Body传输结构,但是打散了内部的传输格式,把内容拆分为二进制帧的格式,HTTP/2把报文的基本传输单位叫做帧,而帧分为两个大类 HEADERS(首部) 和 DATA(消息负载),一个消息划分为两类帧传输同时采用二进制编码。

这种做法类似Chunked化整数据的方式,把Header+body等同的帧数据,同时内部通过类型判断进行标记。

阅读全文 »

费曼学习法有四个关键词:concept「概念」、teach「教授」、review「回顾」、simplify「简化」。如果你不能用简洁的语言把一个概念说明白,说明你没有真正的掌握它。

阅读全文 »

仅仅使用openssl,不依赖其他配置文件,自建CA并签发服务器证书。

CA私钥和自签证书

执行以下命令,回答提示问题,生成CA的证书。

1
2
openssl genrsa -out rootCA.key 4096
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 7000 -out rootCA.crt

rootCA.key 是CA私钥,要存好了。

阅读全文 »

用户往任务队列里面生产任务,线程池从任务队列里面取任务进行消费,让少数线程的线程池来处理大量任务的任务队列,这就是一个生产者和消费者的模型。

阅读全文 »

深入解析RFC 5389定义的四种NAT类型(完全锥形、地址受限锥形、端口受限锥形、对称形)的行为特征与映射机制,基于STUN协议实现系统化的NAT类型检测算法,为P2P网络穿透提供精确的拓扑识别基础

阅读全文 »

rocksDB是什么?

分布式领域的三架马车:GFS、BigTable、MapReduce。
高效发挥存储硬件性能的嵌入式KV存储引擎,是facebook基于google的leveldb发展而来的。

rocksDB解决了什么问题?

写多读少的场景需求,Mysql的InnoDb的B+树是为了解决读多写少的问题。

RockdsDB是怎么解决的?

阅读全文 »

  1. 左值引用和右值引用的区别?右值引用的意义?
    左值引用是对左值的引用,右值引用是对右值的引用。
    功能差异:左值引用是为了避免对象拷贝,如函数传参、函数返回值。右值引用是为了实现移动语义和完美转发。
    怎么区分左值和右值?
    左值可以在等号的左边,可以取地址,具名,比如:变量名、返回左值引用的函数调用、前置自增自减、赋值运算或符号赋值运算、解引用。
    右值只能在等号的右边,不能取地址,不具名,右值有纯右值,比如字面值、返回非引用类型的函数调用、后置自增自减、算术逻辑比较表达式,右值还有将亡值,主要为C++11新引入的与右值引用(移动语义)相关的值类型,可以通过move把左值强制转换成将亡值,将亡值将用来触发移动构造或移动赋值构造,并进行资源转移,最后调用析构函数。
    const的左值引用可以指向右值,但不能修改这个值。右值引用可以通过std::move可以执行左值。声明出来的左值引用和右值引用都是左值。
    移动语义是为了对象赋值时,避免资源的重新分配,比如移动构造和移动拷贝构造,stl的unique_ptr也有用到。
    完美转发指的是,函数模板可以将自己的参数完美的转发给内部调用的其他函数,完美指的是不仅能转发参数的值,还能保证转发时参数的左右值属性保持不变。
    借用万能引用,通过引用的方式来接收左右参数的值。万能引用折叠的原则:参数为左值或者左值引用,T &&将转化为int &(假设传进来的值为int类型),参数为右值或者右值引用,T &&将转化为int &&,std::forward(v),T为左值引用,v将转化为T类型的左值,T为右值引用,v将转化为T类型的右值。
    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
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #include <stdio.h>
    #include <string>
    #include <algorithm>

    using namespace std;

    class ResourceOwner
    {
    public:
    ResourceOwner(const char res[])
    {
    theResource = new string(res);
    }

    ResourceOwner(const ResourceOwner &other)
    {
    printf("copy %s\n", other.theResource->c_str());
    theResource = new string(other.theResource->c_str());
    }

    ResourceOwner(ResourceOwner &&other)
    {
    printf("copy2 %s\n", other.theResource->c_str());
    theResource = other.theResource;
    other.theResource = nullptr;
    }

    ResourceOwner &operator=(const ResourceOwner &other)
    {
    ResourceOwner tmp(other);
    swap(theResource, tmp.theResource);
    printf("assign %s\n", other.theResource->c_str());
    }

    ResourceOwner &operator=(ResourceOwner &&other)
    {
    theResource = other.theResource;
    other.theResource = nullptr;
    printf("assign2 %s\n", theResource->c_str());
    }

    ~ResourceOwner()
    {
    if (theResource)
    {
    printf("destructor %s\n", theResource->c_str());
    delete theResource;
    }
    }

    private:
    string *theResource;
    };

    void testCopy()
    {
    // case 1
    printf("=====start testCopy()=====\n");
    ResourceOwner res1("res1");
    ResourceOwner res2 = res1;
    // copy res1
    printf("=====destructors for stack vars, ignore=====\n");
    }

    void testAssign()
    {
    // case 2
    printf("=====start testAssign()=====\n");
    ResourceOwner res1("res1");
    ResourceOwner res2("res2");
    res2 = res1; // 先调拷贝再调赋值,竟然还要调用一次拷贝!
    // copy res1, assign res1, destrctor res2
    printf("=====destructors for stack vars, ignore=====\n");
    }

    void testRValue()
    {
    // case 3
    printf("=====start testRValue()=====\n");
    ResourceOwner res2("res2");
    res2 = ResourceOwner("res1"); // 先调拷贝再调赋值,加了移动复制运算后,直接移动就行了!
    // copy res1, assign res1, destructor res2, destructor res1
    printf("=====destructors for stack vars, ignore=====\n");
    }

    ResourceOwner getRO()
    {
    ResourceOwner res1("tmp");
    return res1;
    }

    void testFuncReturn()
    {
    // case 4
    printf("=====start testFuncReturn()=====\n");
    ResourceOwner res2("res2");
    res2 = getRO();
    // copy res1, assign res1, destructor res2, destructor res1
    printf("=====destructors for stack vars, ignore=====\n");
    }

    int main()
    {
    testCopy();
    testAssign();
    testRValue();
    testFuncReturn();
    }
    没加移动语义前:

    加了移动语义后:

    返回值不需要std::move,编译器会进行返回值优化。
  2. C++11智能指针以及使用场景?
    指针管理的困境:资源释放了,指针没有置空,比如野指针(资源释放了,但没有置为nullptr,还在继续使用)、指针悬挂(多个指针指向同一个资源,其中一个指针释放了资源也置空了,但是其他的指针并不知道)、踩内存(拿着野指针去修改新分配的资源);没有释放资源,产生内存泄漏;重复释放资源,引发coredump;
    通过RAII的方式来解决,有shared_ptr、weak_ptr和unique_ptr。
    unique_ptr 类似于一个独占所有权的指针,只能有一个指针管理一个对象,当 unique_ptr 被销毁时,它所管理的对象也会被随之销毁。它的定义和用法如下:
1
2
std::unique_ptr<int> ptr(new int(10));
std::cout << *ptr << std::endl;

shared_ptr 是一种共享所有权的指针,多个指针可以同时管理一个对象,当最后一个 shared_ptr 被销毁时,它所管理的对象才会被随之销毁。它的定义和用法如下:

1
2
3
std::shared_ptr<int> ptr1(new int(10));
std::shared_ptr<int> ptr2 = ptr1;
std::cout << *ptr1 << " " << *ptr2 << std::endl;

weak_ptr 是 shared_ptr 的一种扩展,它可以不增加所管理对象的引用计数而与 shared_ptr 交互,但它不能直接访问所管理的对象,需要通过 lock() 函数获得一个指向所管理的对象的 shared_ptr。通常用于解决 shared_ptr 循环引用的问题。它的定义和用法如下:

阅读全文 »

1
https://dev.mysql.com/doc/dev/mysql-server/latest/

初识mysql


按照数据结构来组织、存储和管理数据的仓库。

  • OLTP(on-line transaction processing)联机事务处理,主要数据库增删改查。
  • OLAP(on-line analytical processing)联机分析处理,主要对数据库进行统计分析,为决策提供支持。
    SQL命令:
  • DQL,数据查询语言,select
  • DML(data manipulate language),数据操作语言,插入删除更新记录
  • DDL(data define language),数据定义语言,创建修改删除表
  • DCL(data control language),数据控制语言,授予用户权限、收回用户权限
  • TCL(transaction control language),事务控制语言,事务提交,事务回滚
    连接池模型:
1
2
3
4
5
6
7
8
select(listenfd+1, readfds, NULL, NULL, 0); // 一直阻塞。使用select,fd少,可以跨平台
int clientfd = accept(listenfd, &addr, &len);
mysql_thread_create(key_thread_one_connection, &id, &connection_attrib, handle_connection, (void*)channel_info);

while (1) { // 连接作为单独的session
int n = read(clientfd); // 阻塞的
do_command();
}
阅读全文 »
0%