Nginx 内存池

目录

零、基本框架

一、基础结构 

二、对外接口

三、函数实现

1、ngx_create_pool

2、ngx_destroy_pool

3、ngx_reset_pool

4、ngx_palloc

5、ngx_pnalloc

6、ngx_pmemalign

7、ngx_pfree

8、ngx_pcalloc

9、ngx_pool_cleanup_add

10、ngx_pool_run_cleanup_file

11、相关内存 handler


零、基本框架

        nginx 内存池模块基于链表结构设计,每个链表节点都是一个内存池单元。每个内存池单元通过从操作系统内存中预申请一整片空间,通过移动指针进行内存分配。内存池模块支持动态扩容,在内存池空间不足时,往内存池链表中新增内存池节点。

        对于大内存申请需求,内存池模块设计了 large 链表进行管理。内存池模块 reset 或 destroy 时都会清理 large 链表(调用 free 释放掉)。

        内存池模块分配内存时支持内存对齐,提高 CPU 寻址效率。内存池模块也支持自定义对文件句柄等类似资源的 close handler。

一、基础结构 

/*
 * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
 * On Windows NT it decreases a number of locked pages in a kernel.
 */
#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)

#define NGX_POOL_ALIGNMENT   16

#define NGX_MIN_POOL_SIZE                                                     \
    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \
              NGX_POOL_ALIGNMENT)


typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;
struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};

/*
	大块内存片结构(典型链表结构)
*/
typedef struct ngx_pool_large_s  ngx_pool_large_t;
struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};

/*
	内存池节点描述
*/
typedef struct {
    u_char               *last;  // 未使用内存的起始指针
    u_char               *end;   // 内存池的临界指针 
    ngx_pool_t           *next;  // 下一个内存池的指针
    ngx_uint_t            failed; //分配失败次数
} ngx_pool_data_t;

typedef struct {
    ngx_fd_t              fd;
    u_char               *name;
    ngx_log_t            *log;
} ngx_pool_cleanup_file_t;

/*
	内存池链表主结构
*/
struct ngx_pool_s {
    ngx_pool_data_t       d;    // 内存池链头节点
    size_t                max;  // 内存池可分配的最大内存片大小,超出 max 的分配 ngx_pool_large_t
    ngx_pool_t           *current;  // 当前正在使用的内存池指针(可直接分配内存空间)
    ngx_chain_t          *chain;  //
    ngx_pool_large_t     *large;  // 保存超出 max 大小的内存链
    ngx_pool_cleanup_t   *cleanup; // 自带释放 handler(例如文件句柄的close) 的内存链
    ngx_log_t            *log;
};

二、对外接口

// 新建内存池
ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
// 销毁内存池
void ngx_destroy_pool(ngx_pool_t *pool);
// 重置内存池,释放 large 链表
void ngx_reset_pool(ngx_pool_t *pool);

/*
	从内存池中申请 size 大小的内存,内存按平台位数对齐。
	小于 max 的内存,直接从内存池中分配;
	大于 max 的内存,动态申请,并记录在 large 链表中
*/
void *ngx_palloc(ngx_pool_t *pool, size_t size);
// 同 ngx_palloc,但不进行内存对齐
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);

/*
	调用 ngx_palloc 分配内存,并初始化为 0 
*/
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);

/*
	从系统申请一块内存对齐的内存,并保存在 large 链表中
*/
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);

/*
	释放保存在 large 链表中的 p 地址内存
*/
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);

/*
    从内存池中申请 size 大小的内存,并可以通过调用自定义的回调函数进行释放
*/
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);

/*
	释放 cleanup 中包含文件句柄 fd 的节点
*/
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);

/*
    cleanup 节点中的资源释放 handler,同 close,释放句柄
*/
void ngx_pool_cleanup_file(void *data);

/*
    cleanup 节点中资源释放 handler,同 unlink,删除文件
*/
void ngx_pool_delete_file(void *data);

三、函数实现

1、ngx_create_pool

        新建内存池

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
    ngx_pool_t  *p;
	// 申请按 NGX_POOL_ALIGNMENT 内存对齐 size 大小的内存空间
    // linux 下主要依赖 posix_memalign 和 memalign 两个函数
    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
        return NULL;
    }
	// 该内存池可分配内存的起始指针
    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
	// 该内存池可分配内存的临界指针
    p->d.end = (u_char *) p + size;
	// 初始化时,内存池链表只有一个节点
    p->d.next = NULL;
	// 内存分配失败次数
    p->d.failed = 0;
	// 当前内存池可分配的内存总量
    size = size - sizeof(ngx_pool_t);
	// 内存池可分配的最大内存为 pagesize - 1
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
	// 初始化时,可使用的内存池只有当前头部节点
    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;
    p->log = log;

    return p;
}

2、ngx_destroy_pool

        销毁内存池链表

void
ngx_destroy_pool(ngx_pool_t *pool)
{
    ngx_pool_t          *p, *n;
    ngx_pool_large_t    *l;
    ngx_pool_cleanup_t  *c;

	// 遍历内存池 cleanup 链表,按照定义的 handler 函数释放全部空间
    for (c = pool->cleanup; c; c = c->next) {
        if (c->handler) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "run cleanup: %p", c);
            c->handler(c->data);
        }
    }
// 一些 debug 信息,用于确认内存池的使用状况
#if (NGX_DEBUG)

    /*
     * we could allocate the pool->log from this pool
     * so we cannot use this log while free()ing the pool
     */

    for (l = pool->large; l; l = l->next) {
        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
    }

    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                       "free: %p, unused: %uz", p, p->d.end - p->d.last);

        if (n == NULL) {
            break;
        }
    }

#endif
	// 释放全部大块内存(申请超过 max 的内存均在此链表)
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }
	// 自头节点,遍历所有内存池,释放空间
    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
        ngx_free(p);

        if (n == NULL) {
            break;
        }
    }
}

3、ngx_reset_pool

        重置内存池,不是释放该空间,而且调整内存池可用范围,重复使用内存池空间。

        nginx 内存池模块没有设计类似 free 的内存返还函数,这边避免了繁琐的内存碎片管理逻辑。在需要时直接重置 ngx_reset_pool 即可。

/*
	需要注意,内存池空间是预申请的一整块空间。
	重置内存池,不是释放该空间,而且调整内存池可用范围,重复使用内存池空间。
*/
void
ngx_reset_pool(ngx_pool_t *pool)
{
    ngx_pool_t        *p;
    ngx_pool_large_t  *l;
	
	// 重置内存池会释放掉所有的大块内存
    for (l = pool->large; l; l = l->next) {
        if (l->alloc) {
            ngx_free(l->alloc);
        }
    }
	
	// 遍历所有的内存池节点,修改可分配空间的起始指针
    for (p = pool; p; p = p->d.next) {
        p->d.last = (u_char *) p + sizeof(ngx_pool_t);
        p->d.failed = 0;
    }

	// 重置 current 指针
    pool->current = pool;
    pool->chain = NULL;
    pool->large = NULL;
}

4、ngx_palloc

        按调用顺序,从上到下看。

/*
	从内存池中申请 size 大小的内存,内存按平台位数对齐。
	小于 max 的内存,直接从内存池中分配;
	大于 max 的内存,动态申请,并记录在 large 链表中
*/
void *
ngx_palloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        return ngx_palloc_small(pool, size, 1);
    }
#endif

    return ngx_palloc_large(pool, size);
}

        申请小于 max 的内存,直接从内存池中划分

/*
	分配内存池中 size       大小的空间,align 标识是否进行内存对齐
*/
static ngx_inline void *
ngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)
{
    u_char      *m;
    ngx_pool_t  *p;
	// 从当前可用内存池节点中进行分配
    p = pool->current;

    do {
		// 分配的内存的指针
        m = p->d.last;

		// 内存按平台字长对齐(64 bit)
        if (align) {
            m = ngx_align_ptr(m, NGX_ALIGNMENT);
        }

		// 若当前内存池空间足够则直接进行分配
        if ((size_t) (p->d.end - m) >= size) {
            p->d.last = m + size;

            return m;
        }

		// 当前内存池空间不足,则向下寻找可供分配的内存池
        p = p->d.next;

    } while (p);

	// 若当前内存池链都不存在满足 size 大小的空间,则新建内存池
    return ngx_palloc_block(pool, size);
}

        新增内存池节点,并返回申请的内存指针

/*
	新增内存池节点
*/
static void *
ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
	// 分配的内存指针
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new;
	// 获取内存池大小,初始化时指定
    psize = (size_t) (pool->d.end - (u_char *) pool);
	// 分配 psize 内存池空间
    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
    if (m == NULL) {
        return NULL;
    }
	// 对新内存池的一些参数进行初始化赋值
    new = (ngx_pool_t *) m;

    new->d.end = m + psize;
    new->d.next = NULL;
    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);
	// 内存对齐
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
	// 分配出去后,调整可用内存范围
    new->d.last = m + size;
	/*
		因为刚刚分配失败了,新建了内存池节点,所以需要遍历内存池链,
		刷新下每个节点的可用状态,确定新的 current 内存池节点
		current 记录的内存池节点,是可直接使用分配内存的节点,不然每次遍历链表搜索是很耗时的。
		对于失败次数太多的节点(超过 4 次),需要封存,将 current 指针向下移动。
	*/
	
    for (p = pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            pool->current = p->d.next;
        }
    }

	// 将新增的内存池节点加到内存池链表末尾
    p->d.next = new;

    return m;
}

        申请大于 max 的内存空间,则单独从操作系统内存中额外申请,并记录在 large 链表中

/*
	申请大于 max 的内存空间,则单独从操作系统内存中额外申请,并记录在 large 链表中
*/
static void *
ngx_palloc_large(ngx_pool_t *pool, size_t size)
{
    void              *p;
    ngx_uint_t         n;
    ngx_pool_large_t  *large;
	// 该函数调用了 malloc 申请了内存
    p = ngx_alloc(size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    n = 0;
	/*
		将新申请的内存空间,保存在 large 链表中
	*/
	// 1、先遍历 large 链表最前面的三个节点,若存在内存已经被 free 的,则保存在该节点
    for (large = pool->large; large; large = large->next) {
        if (large->alloc == NULL) {
            large->alloc = p;
            return p;
        }
		// 往后找三个节点
        if (n++ > 3) {
            break;
        }
    }
	// 2、前三个都没有被 free,则新建 large 节点,将新申请的大内存保存在该节点
    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }
	// 3、将新建的 large 节点作为新的头节点
    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

5、ngx_pnalloc

        与 ngx_palloc 类似,唯一区别在于申请的小于 max 的内存不会对齐。

void *
ngx_pnalloc(ngx_pool_t *pool, size_t size)
{
#if !(NGX_DEBUG_PALLOC)
    if (size <= pool->max) {
        // 不对齐
        return ngx_palloc_small(pool, size, 0);
    }
#endif

    return ngx_palloc_large(pool, size);
}

6、ngx_pmemalign

        从系统申请一块内存对齐的内存,并保存在 large 链表中

/*
	从系统申请一块内存对齐的内存,并保存在 large 链表中
*/
void *
ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
{
    void              *p;
    ngx_pool_large_t  *large;

	// 申请一块 size 大小的内存,内存对齐
    p = ngx_memalign(alignment, size, pool->log);
    if (p == NULL) {
        return NULL;
    }

    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);
    if (large == NULL) {
        ngx_free(p);
        return NULL;
    }
	// 添加到 large 链表头部
    large->alloc = p;
    large->next = pool->large;
    pool->large = large;

    return p;
}

7、ngx_pfree

        释放保存在 large 链表中的 p 地址内存

/*
	释放保存在 large 链表中的 p 地址内存
*/
ngx_int_t
ngx_pfree(ngx_pool_t *pool, void *p)
{
    ngx_pool_large_t  *l;

    for (l = pool->large; l; l = l->next) {
		// 搜索地址为 p 的内存
        if (p == l->alloc) {
            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
                           "free: %p", l->alloc);
			// 同 free
            ngx_free(l->alloc);
            l->alloc = NULL;

            return NGX_OK;
        }
    }

    return NGX_DECLINED;
}

8、ngx_pcalloc

调用 ngx_palloc 分配内存,并初始化为 0

/*
	调用 ngx_palloc 分配内存,并初始化为 0 
*/
void *
ngx_pcalloc(ngx_pool_t *pool, size_t size)
{
    void *p;

    p = ngx_palloc(pool, size);
    if (p) {
		// 同 memset(p, 0, size)
        ngx_memzero(p, size);
    }

    return p;
}

9、ngx_pool_cleanup_add

        从内存池中申请 size 大小的内存,并可以通过调用自定义的回调函数进行释放,例如文件描述符等资源。

/*
	区别与申请内存后只能调用 free 进行释放,
	该函数申请的内存可以通过调用定义的回调函数进行释放,例如文件描述符等资源
*/
ngx_pool_cleanup_t *
ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
{
    ngx_pool_cleanup_t  *c;
	// 新建 ngx_pool_cleanup_t 节点
    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
    if (c == NULL) {
        return NULL;
    }
	// 从内存池中申请 size 大小的内存空间
    if (size) {
        c->data = ngx_palloc(p, size);
        if (c->data == NULL) {
            return NULL;
        }

    } else {
        c->data = NULL;
    }
	
    c->handler = NULL;
	// 插到 cleanup 链表头节点
    c->next = p->cleanup;

    p->cleanup = c;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);

    return c;
}

10、ngx_pool_run_cleanup_file

        释放 cleanup 中包含文件句柄 fd 的节点

/*
	释放 cleanup 中包含文件句柄 fd 的节点
*/
void
ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
{
    ngx_pool_cleanup_t       *c;
    ngx_pool_cleanup_file_t  *cf;

    for (c = p->cleanup; c; c = c->next) {
		// 关闭文件句柄的 close 函数 handler
        if (c->handler == ngx_pool_cleanup_file) {

            cf = c->data;
			// 搜索 fd 句柄
            if (cf->fd == fd) {
                c->handler(cf);
                c->handler = NULL;
                return;
            }
        }
    }
}

11、相关内存 handler


void
ngx_pool_cleanup_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
                   c->fd);
	// close 函数
    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}

void
ngx_pool_delete_file(void *data)
{
    ngx_pool_cleanup_file_t  *c = data;

    ngx_err_t  err;

    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
                   c->fd, c->name);

	// unlink 函数
    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
        err = ngx_errno;

        if (err != NGX_ENOENT) {
            ngx_log_error(NGX_LOG_CRIT, c->log, err,
                          ngx_delete_file_n " \"%s\" failed", c->name);
        }
    }

    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", c->name);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flask python 开发篇:上传文件(在指定目录下创建文件夹)

flask上传文件以及常见问题 一、flask文档上传文件的介绍二、上传文件的实现2.1、生成一个from表单&#xff0c;用来提交图片2.2、编写上传的逻辑 三、运行代码、以及常见异常四、写在最后 一、flask文档上传文件的介绍 Flask上传文件的文档介绍&#xff0c;文件上传的基本思想…

基于nodejs+vue的nuct产品售后管理系统python-flask-django-php

同时还能为用户提供一个方便实用的nuct产品售后管理系统&#xff0c;使得用户能够及时地找到合适自己的产品。管理员在使用本系统时&#xff0c;可以通过后台管理员界面管理用户的信息&#xff0c;也可以发布产品售后信息&#xff0c;让用户及时了解nuct产品售后信息。这样&…

web前端之3D标签动画、指定范围的随机数、动态设置css变量、文档片段对象、反向动画

MENU 效果图htmlJavaScriptstyle 效果图 html <div class"container"></div>JavaScript // 祝词 var words [健康码常绿,股票飙红,生意兴隆,财源广进,心想事成,永远十八,身体健康,大富大贵,大吉大利,万事如意,美梦成真,吉祥如意,鸿运当头,五福临门,吉…

uniapp的配置文件、入口文件、主组件、页面管理部分

pages.json 配置文件&#xff0c;全局页面路径配置&#xff0c;应用的状态栏、导航条、标题、窗口背景色设置等 main.js 入口文件&#xff0c;主要作用是初始化vue实例、定义全局组件、使用需要的插件如 vuex&#xff0c;注意uniapp无法使用vue-router&#xff0c;路由须在pag…

每日一练:LeeCode-200、岛屿数量【DFS递归+BFS队列】

给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维网格&#xff0c;请你计算网格中岛屿的数量。 岛屿总是被水包围&#xff0c;并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。 此外&#xff0c;你可以假设该网格的四条边…

生物信息—数据库

文章目录 核酸数据库1 一级核酸数据库&#xff1a;GenBank1.1 原核生物核酸序列1.2 真核生物成熟mRNA1.3 真核生物DNA序列 2 一级核酸数据库&#xff1a;基因组数据库&#xff1a;Ensemble3 一级核酸数据库&#xff1a;微生物宏基因组数据库&#xff1a;JCVI4 二级核酸数据库 蛋…

蓝桥杯练习07小兔子爬楼梯

小兔子爬楼梯 介绍 小兔子想去月球上旅行&#xff0c;假设小兔子拥有一个阶梯子&#xff0c;当你爬完层就可以到达月球&#xff0c;小兔子每次可以跳1或者2个台阶&#xff0c;小兔子有多少种跳法可以到达月球呢&#xff1f; 给定n是一个正整数&#xff0c;代表梯子的阶数&…

数据可视化基础与应用-04-seaborn库从入门到精通03

总结 本系列是数据可视化基础与应用的第04篇seaborn&#xff0c;是seaborn从入门到精通系列第3篇。本系列的目的是可以完整的完成seaborn从入门到精通。主要介绍基于seaborn实现数据可视化。 参考 参考:数据可视化-seaborn seaborn从入门到精通03-绘图功能实现01-关系绘图 …

在ubuntu22.04系统上用pycharm编写第一个ros2程序

1.打开终端&#xff08;快捷键altctrlt&#xff09;&#xff0c;创建工作空间&#xff0c;工作空间就是文件夹 2.创建一个功能包 打开pycharm的终端&#xff08;altf12&#xff09; 3.创建节点文件 在village_li文件夹右键新建li4.py 4.在li4.py编写代码 5.在setup.py里面添加…

http模块 获取http请求报文中的路径 与 查询字符串

虽然request.url已包含属性和查询字符串&#xff0c;但使用不便&#xff0c;若只需其中一个不好提取&#xff0c;于是用到了如下路径和字符串的单独查询方法&#xff1a; 一、获取路径 例如&#xff1a;我在启动谷歌端口时输入http://127.0.0.1:9000 后接了 "/search?k…

一文解释python中的实例方法,类方法和静态方法作用和区别是啥?该如何使用

我们都知道 &#xff0c;python类中有三种常见的方法 &#xff0c;分别是实例方法 &#xff0c;类方法和静态方法 。那么这几个方法到底有什么作用 &#xff1f; 它们之间有什么区别 &#xff1f;该如何使用 &#xff1f; 带着这些问题 &#xff0c;下面我们就来了解下这三种方…

基于FPGA实现的自适应三速以太网

一、三速以太网 千兆以太网PHY芯片是适配百兆和十兆的&#xff0c;十兆就不管了&#xff0c;我们的设计只适应千兆和百兆。 根据上图&#xff0c;我们是可以获取当前主机网口的速率信息的。 always(posedge w_rxc_bufr) beginif(w_rec_valid d0) beginro_speed < w_rec_…

Ubuntu系统部署Inis博客结合内网穿透实现公网访问本地站点

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

java一和零(力扣Leetcode474)

一和零 力扣原题 给定一个二进制字符串数组 strs 和两个整数 m 和 n&#xff0c;请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中最多有 m 个 0 和 n 个 1。 示例 1&#xff1a; 输入&#xff1a;strs [“10”, “0001”, “111001”, “1”, “0”], m 5, n …

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能 文章目录 RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能1. RabbitMQ端口说明2. 开启Rabbitmq后台管理功能1. 查看rabbitmq已安装的插件2. 开启rabbitmq后台管理平台插件3. 开启插件后&#xff0c;再次查看插…

itextPdf生成pdf简单示例

文章环境 jdk1.8&#xff0c;springboot2.6.13 POM依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.ite…

Ruoyi若依框架下载流程详细解读(SpringBoot-Vue)

图解&#xff1a; 前端设计&#xff1a; 前端设计一个link文字连接或者按钮&#xff08;ElementUI&#xff09;Element - The worlds most popular Vue UI framework 前端请求设计&#xff1a; import request from /utils/request //下载示例模型定义语言的JSON export const…

pe启动盘破解windows密码wins电脑登录密码修改重置

目录 1.进入电脑BIOS&#xff0c;设置电脑第一启动项为U盘启动2.进入微pe系统3.然后点击界面最左下方的Windows图标4.点击windows密码选择对应用户名称修改&#xff1b; 1.进入电脑BIOS&#xff0c;设置电脑第一启动项为U盘启动 把u盘插到要清除密码的电脑&#xff0c;然后开机…

基于nodejs+vue基于hive旅游数据的分析与应用python-flask-django-php

系统阐述的是使用基于hive旅游数据的分析与应用系统&#xff0c;对于nodejs结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了express框架和MySql数据库技术搭建系统的整体架构。利用…

原生 HTML/CSS/JS 实现右键菜单和二级菜单

文章来源&#xff1a;www.huhailong.vip 站点 文章源地址&#xff1a;https://www.huhailong.vip/article/1764653112011841538 Demo效果演示地址 先看效果图 {{{width“auto” height“auto”}}} 需要注意的就是边界检测处理&#xff0c;到极端点击底部和右侧时如果不做处理会…