深入理解ngx_http_upstream_vnswrr_module负载均衡模块

目录

  • 1. 引言
  • 2. 启用vnswrr负载均衡模块
  • 3. 源码剖析
    • 3.1 配置指令分析
    • 3.2 负载均衡算法配置初始化
    • 3.3 负载均衡请求上下文的初始化
    • 3.4 获取peer

1. 引言

   之前有讨论了nginx的swrr算法的两个问题,并引出了阿里tengine的vnswrr算法如何来克服swrr的问题。本文通过源码层面对ngx_http_upstream_vnswrr_module模块进行分析,来深入理解vnswrr负载均衡算法。关于swrr算法的思考可以查看《nginx upstream server主动健康检测模块添加https检测功能》。关于vnswrr的算法原理可以参考《阿里七层流量入口负载均衡算法演变之路》。

2. 启用vnswrr负载均衡模块

  配置指令的格式为:

指令:    vnswrr  [max_init=init_vode_num]
默认值:  -
上下文:   upstream

  其中init_vnode_num是初始化虚拟节点的数量,具体可以参考《阿里七层流量入口负载均衡算法演变之路》中**接入层 VNSWRR 算法(V2)**部分的描述。

  以5台rs服务器为例开启vnswrr,距离如下:

upstream {vnswrr 5;server 192.168.0.1 weight=1;server 192.168.0.2 weight=1;server 192.168.0.3 weight=3;server 192.168.0.4 weight=3;server 192.168.0.5 weight=5;server 192.168.0.6 weight=5;
}

3. 源码剖析

3.1 配置指令分析

   本模块定义了配置指令vnswrr,代码如下:

static ngx_command_t  ngx_http_upstream_vnswrr_commands[] = {{ ngx_string("vnswrr"),NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,ngx_http_upstream_vnswrr,0,0,NULL },ngx_null_command
};

  以上定义了指令分析回调函数ngx_http_upstream_vnswrr, 其源码如下:

static char *
ngx_http_upstream_vnswrr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ngx_http_upstream_srv_conf_t            *uscf;ngx_http_upstream_vnswrr_srv_conf_t     *uvnscf;ngx_str_t                               *value;ngx_int_t                                max_init;uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);if (uscf->peer.init_upstream) {ngx_conf_log_error(NGX_LOG_WARN, cf, 0,"load balancing method redefined");}/* 将vnswrr的负载均衡算法配置初始化回调函数挂进去 */uscf->peer.init_upstream = ngx_http_upstream_init_vnswrr;/* 不象哈希负载均衡算法,本算法可以支持主备服务器 */uscf->flags = NGX_HTTP_UPSTREAM_CREATE|NGX_HTTP_UPSTREAM_WEIGHT|NGX_HTTP_UPSTREAM_BACKUP|NGX_HTTP_UPSTREAM_MAX_FAILS|NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
#if defined(nginx_version) && nginx_version >= 1011005|NGX_HTTP_UPSTREAM_MAX_CONNS
#endif|NGX_HTTP_UPSTREAM_DOWN;/* 获取vnswrr的配置上下文 */uvnscf = ngx_http_conf_upstream_srv_conf(uscf,ngx_http_upstream_vnswrr_module);value = cf->args->elts;max_init = 0;/* 如果有max_init参数,就从配置指令中解析初始虚拟节点数量 */if (cf->args->nelts > 1) {if (ngx_strncmp(value[1].data, "max_init=", 9) == 0) {max_init = ngx_atoi(&value[1].data[9], value[1].len - 9);if (max_init == NGX_ERROR) {ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,"invalid parameter \"%V\"", &value[1]);return NGX_CONF_ERROR;}}}uvnscf->max_init = max_init;return NGX_CONF_OK;
}

3.2 负载均衡算法配置初始化

  nginx在解析完配置文件后,会为每个upstream调用前面设置好的init_upstream回调函数来初始化设置好的负载均衡算法,对于开启了vnswrr算法,则会回调ngx_http_upstream_init_vnswrr函数,该回调由3.1节中ngx_http_upstream_vnswrr函数设置。下面来分析一下ngx_http_upstream_init_vnswrr函数:

static ngx_int_t
ngx_http_upstream_init_vnswrr(ngx_conf_t *cf,ngx_http_upstream_srv_conf_t *us)
{ngx_http_upstream_rr_peers_t           *peers, *backup;ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf, *ubvnscf;ngx_http_upstream_server_t             *server;ngx_uint_t                              i, g, bg, max_init;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "init vnswrr");/* 借用round-robin的ngx_http_upstream_init_round_robin初始化peer链表 */if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {return NGX_ERROR;}/* 对于配置的每个server(包括主和备),计算配置的所有server权重的最大公约数 */g = 0;bg = 0;if (us->servers) {server = us->servers->elts;for (i = 0; i < us->servers->nelts; i++) {if (server[i].backup) {bg = ngx_http_upstream_gcd(bg, server[i].weight);} else {g = ngx_http_upstream_gcd(g , server[i].weight);}}}if (g == 0) {g = 1;}if (bg == 0) {bg = 1;}uvnscf = ngx_http_conf_upstream_srv_conf(us,ngx_http_upstream_vnswrr_module);if (uvnscf == NULL) {return NGX_ERROR;}peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;max_init = uvnscf->max_init;/* init_number为初始虚拟节点的序号last_number为最后一次分配的虚拟节点的序号last_peer为最后一次分配的peer的指针*/uvnscf->init_number = NGX_CONF_UNSET_UINT;uvnscf->last_number = NGX_CONF_UNSET_UINT;uvnscf->last_peer = NULL;uvnscf->next = NULL;uvnscf->gcd = g;/* 如果没有配置max_init,则设置为peer的数量max_init最大为总的权重*/if (!max_init) {uvnscf->max_init = peers->number;} else if (max_init > peers->total_weight) {uvnscf->max_init = peers->total_weight;}/* 设置负载均衡请求上下文初始化回调函数 */us->peer.init = ngx_http_upstream_init_vnswrr_peer;/* 如果upstream是配置成带权重模式的,即所有服务器的weight不都等于1,则走正常vnswrr算法,否则,退化为简单的round-robin算法。对于vnswrr,需要分配虚拟节点并进行初始化,虚拟节点的数量是总权重除以上面算出的最大公约数。稍微思考一下,就知道这个是合理的,譬如三台server,他们的权重都分别是2,4,6,那么其效果和1,2,3是一样的,所以找到最大公约数,并把这个最大公约数除掉以后得到有效权重。*/if (peers->weighted) {uvnscf->vpeers = ngx_pcalloc(cf->pool,sizeof(ngx_http_upstream_rr_vpeers_t)* peers->total_weight / uvnscf->gcd);if (uvnscf->vpeers == NULL) {return NGX_ERROR;}/* 初始化一批虚拟节点,最多是max_init个虚拟节点,避免一次性初始化大量的虚拟节点当值nginx的cpu突发overload*/ngx_http_upstream_init_virtual_peers(peers, uvnscf, 0, uvnscf->max_init);}/* 下面是backup服务器部分的初始化逻辑,和主服务器是一样的 */backup = peers->next;if (backup) {ubvnscf = ngx_pcalloc(cf->pool,sizeof(ngx_http_upstream_vnswrr_srv_conf_t));if (ubvnscf == NULL) {return NGX_ERROR;}ubvnscf->init_number = NGX_CONF_UNSET_UINT;ubvnscf->last_number = NGX_CONF_UNSET_UINT;ubvnscf->last_peer = NULL;ubvnscf->gcd = bg;ubvnscf->max_init = max_init;if (!max_init) {ubvnscf->max_init = backup->number;} else if (max_init > backup->total_weight) {ubvnscf->max_init = backup->total_weight;}/* 把主服务器和backup服务器链起来 */uvnscf->next = ubvnscf;if (!backup->weighted) {return NGX_OK;}ubvnscf->vpeers = ngx_pcalloc(cf->pool,sizeof(ngx_http_upstream_rr_vpeers_t)* backup->total_weight / ubvnscf->gcd);if (ubvnscf->vpeers == NULL) {return NGX_ERROR;}ngx_http_upstream_init_virtual_peers(backup, ubvnscf, 0, ubvnscf->max_init);}return NGX_OK;
}

  ngx_http_upstream_init_vnswrr函数的逻辑就是分别对主服务器和备服务器组进行加载操作,初始化一部分虚拟节点,详细的逻辑在源码中已经进行了注释,不再赘述。

3.3 负载均衡请求上下文的初始化

  当nginx接收到http请求需要连接上游服务器的时候,就会发起负载均衡请求上下文的初始化回调,对于vnswrr算法就是回调ngx_http_upstream_init_vnswrr_peer函数了。

static ngx_int_t
ngx_http_upstream_init_vnswrr_peer(ngx_http_request_t *r,ngx_http_upstream_srv_conf_t *us)
{ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf;ngx_http_upstream_vnswrr_peer_data_t   *vnsp;uvnscf = ngx_http_conf_upstream_srv_conf(us,ngx_http_upstream_vnswrr_module);/* 创建请求上下文并进行初始化设置 */vnsp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_vnswrr_peer_data_t));if (vnsp == NULL) {return NGX_ERROR;}vnsp->uvnscf = uvnscf;r->upstream->peer.data = &vnsp->rrp;/* 因为本模块是依赖于round-robin模块的,譬如上游服务器的已分配状态等,这里也需要调用ngx_http_upstream_init_round_robin_peer进行初始化 */if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {return NGX_ERROR;}/* 设置获取peer的回调 */r->upstream->peer.get = ngx_http_upstream_get_vnswrr_peer;return NGX_OK;
}

  这里最关键的就是设置了获取peer的回调函数ngx_http_upstream_get_vnswrr_peer。

3.4 获取peer

  一切准备就绪后,nginx会在请求上游连接的时候调用ngx_event_connect_peer,而在ngx_event_connect_peer函数中将回调ngx_http_upstream_get_vnswrr_peer函数来获取目的服务器的地址信息。接下来来详细分析这个函数,源码如下:

static ngx_int_t
ngx_http_upstream_get_vnswrr_peer(ngx_peer_connection_t *pc, void *data)
{ngx_http_upstream_vnswrr_peer_data_t  *vnsp = data;ngx_int_t                              rc;ngx_uint_t                             i, n;ngx_http_upstream_rr_peer_t           *peer;ngx_http_upstream_rr_peers_t          *peers;ngx_http_upstream_rr_peer_data_t      *rrp;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,"get vnswrr peer, try: %ui", pc->tries);pc->cached = 0;pc->connection = NULL;rrp = &vnsp->rrp;peers = rrp->peers;ngx_http_upstream_rr_peers_wlock(peers);   /* 共享内存加写锁 */if (peers->single) {/*对于只有一个peer的情况,如果这个peer没有down且连接数没有超过限制,则直接分配这个peer*/peer = peers->peer;if (peer->down) {goto failed;}#if defined(nginx_version) && nginx_version >= 1011005if (peer->max_conns && peer->conns >= peer->max_conns) {goto failed;}
#endif#if (NGX_HTTP_UPSTREAM_CHECK)if (ngx_http_upstream_check_peer_down(peer->check_index)) {goto failed;}
#endifrrp->current = peer;} else {/* 如果有多个peer,则调用ngx_http_upstream_get_vnswrr获取peer信息 */peer = ngx_http_upstream_get_vnswrr(vnsp);if (peer == NULL) {goto failed;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,"get vnswrr peer, current: %p %i",peer, peer->current_weight);}/* 将分配到的peer的地址写入到ngx_peer_connection_t(pc_中 */pc->sockaddr = peer->sockaddr;pc->socklen = peer->socklen;pc->name = &peer->name;
#if (T_NGX_HTTP_DYNAMIC_RESOLVE)pc->host = &peer->host;
#endif    peer->conns++;/* 释放上面加的写锁 */ngx_http_upstream_rr_peers_unlock(peers);return NGX_OK;failed:/* 主服务器分配失败了,如果有备服务器,那么从备服务器进行分配 */if (peers->next) {ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, "backup servers");/* 切换到备服务器组 */rrp->peers = peers->next;vnsp->uvnscf = vnsp->uvnscf ? vnsp->uvnscf->next : vnsp->uvnscf;n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))/ (8 * sizeof(uintptr_t));for (i = 0; i < n; i++) {rrp->tried[i] = 0;}/* 释放上面加的写锁 */ngx_http_upstream_rr_peers_unlock(peers);/* 递归调用本函数自己,重新进行一次获取peer的操作 */rc = ngx_http_upstream_get_vnswrr_peer(pc, vnsp);/* 备服务器也分配失败,则返回NGX_BUSY */if (rc != NGX_BUSY) {return rc;}/* 重新加上写锁,在返回前释放 */ngx_http_upstream_rr_peers_wlock(peers);}/* 释放上面加的写锁 */ngx_http_upstream_rr_peers_unlock(peers);pc->name = peers->name;return NGX_BUSY;
}

  本函数针对如果只有一个peer的情况来说,就不需要再进行vnswrr算法了,反过来则进行vnswrr的分配操作,vnswrr算法调用了ngx_http_upstream_get_vnswrr函数进行实际的分配工作。下面就是vnswrr的最核心的代码了,源码如下:

static ngx_http_upstream_rr_peer_t *
ngx_http_upstream_get_vnswrr(ngx_http_upstream_vnswrr_peer_data_t  *vnsp)
{time_t                                  now;uintptr_t                               m;ngx_uint_t                              i, n, p, flag, begin_number;ngx_http_upstream_rr_peer_t            *peer, *best;ngx_http_upstream_rr_peers_t           *peers;ngx_http_upstream_rr_vpeers_t          *vpeers;ngx_http_upstream_rr_peer_data_t       *rrp;ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf;now = ngx_time();best = NULL;#if (NGX_SUPPRESS_WARN)p = 0;
#endifrrp = &vnsp->rrp;peers = rrp->peers;uvnscf = vnsp->uvnscf;vpeers = uvnscf->vpeers;/* last_number == NGX_CONF_UNSET_UINT表示本worker进程第一次进入到ngx_http_upstream_get_vnswrr函数,这里通过将init_number设置为一个随机值来避免多进程产生的“共振”效应。初始化随机值这个机制在《阿里七层流量入口负载均衡算法演变之路》中有提到*/if (uvnscf->last_number == NGX_CONF_UNSET_UINT) {uvnscf->init_number = ngx_random() % peers->number;/* 如果是带权重模式,则使用了虚拟节点来进行负载均衡,所以从虚拟节点中选取peer*/if (peers->weighted) {peer = vpeers[uvnscf->init_number].vpeer;} else {/* 如果是不带权重的模式,则没有虚拟节点,需要直接在peers列表中循环init_number次数,选择第nit_number个peer*/for (peer = peers->peer, i = 0; i < uvnscf->init_number; i++) {peer = peer->next;}}uvnscf->last_number = uvnscf->init_number;uvnscf->last_peer = peer;}if (peers->weighted) {/* 如果当前初始化好的虚拟节点已经都被分配过一次了,并且还有没初始化过的虚拟节点,则再次分配虚拟节点,最多max_init个。 */if (uvnscf->vnumber != peers->total_weight / uvnscf->gcd&& (uvnscf->last_number + 1 == uvnscf->vnumber)){n = peers->total_weight / uvnscf->gcd - uvnscf->vnumber;if (n > uvnscf->max_init) {n = uvnscf->max_init;}ngx_http_upstream_init_virtual_peers(peers, uvnscf, uvnscf->vnumber,n + uvnscf->vnumber);}/* 在虚拟节点循环队列中分配下一个vpeerbegin_numer为当前分配的虚拟节点在虚拟节点循环队列中的序号*/ begin_number = (uvnscf->last_number + 1) % uvnscf->vnumber;peer = vpeers[begin_number].vpeer;} else {/* 如果是不带权重模式,那么直接通过peer链进行peer的分配一个peer中有多个地址的,那么先分配这个peer的地址,否则,找下一个peer,begin_number为当前分配的peer在peer列表中的序号*/if (uvnscf->last_peer && uvnscf->last_peer->next) {begin_number = (uvnscf->last_number + 1) % peers->number;peer = uvnscf->last_peer->next;} else {begin_number = 0;peer = peers->peer;}}/* 以下对上面分配的peer进行状态过滤,如果分配的peer不能用,需要再往下循环获取下一个peer *//* 这里 i != begin_number || flag的判断用来检测是否已经循环了一圈回来了循环了一圈回来的,那么所有的peer就已经遍历了,还是不能满足分配的需要。*/for (i = begin_number, flag = 1; i != begin_number || flag;i = peers->weighted? ((i + 1) % uvnscf->vnumber) : ((i + 1) % peers->number),peer = peers->weighted? vpeers[i].vpeer : (peer->next ? peer->next : peers->peer)){flag = 0;if (peers->weighted) {/* 这里也有可能分配的虚拟节点已经被遍历过一次了,并且还有没初始化过的虚拟节点,则再次分配虚拟节点,最多max_init个 */n = peers->total_weight / uvnscf->gcd - uvnscf->vnumber;if (n > uvnscf->max_init) {n = uvnscf->max_init;}if (n > 0) {ngx_http_upstream_init_virtual_peers(peers, uvnscf, uvnscf->vnumber,n + uvnscf->vnumber);}n = vpeers[i].rindex / (8 * sizeof(uintptr_t));m = (uintptr_t) 1 << vpeers[i].rindex % (8 * sizeof(uintptr_t));} else {n =  i / (8 * sizeof(uintptr_t));m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));}/* 节点是否已经分配过的状态判断 */if (rrp->tried[n] & m) {continue;}/* 节点是否已经被设置为down状态判断 */if (peer->down) {continue;}/* 节点是否故障保护状态判断 */if (peer->max_fails&& peer->fails >= peer->max_fails&& now - peer->checked <= peer->fail_timeout){continue;}/* 节点的当前在线连接是否超过限制判断 */
#if defined(nginx_version) && nginx_version >= 1011005if (peer->max_conns && peer->conns >= peer->max_conns) {continue;}
#endif#if (NGX_HTTP_UPSTREAM_CHECK)if (ngx_http_upstream_check_peer_down(peer->check_index)) {continue;}
#endif/*  得到了分配好的节点 */best = peer;uvnscf->last_peer = peer;uvnscf->last_number = i;p = i;break;}if (best == NULL) {return NULL;}rrp->current = best;/* 在tried位表中设置当前节点已经被分配过 */if (peers->weighted) {n = vpeers[p].rindex / (8 * sizeof(uintptr_t));m = (uintptr_t) 1 << vpeers[p].rindex % (8 * sizeof(uintptr_t));} else {n = p / (8 * sizeof(uintptr_t));m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));}rrp->tried[n] |= m;if (now - best->checked > best->fail_timeout) {best->checked = now;}return best;
}

  以上函数中,如果是不带权重的模式,那么就是最简单的round-robin分配机制,每次分配就循环往后前进一个peer,一个特别的地方就是第一次分配的时候设置了一个随机值位置,从这个随机位置开始进行正式分配,避免产生“共振”;如果是带权重的模式,那么才是真正的vnswrr算法,这个算法另外创建了虚拟节点,虚拟节点的总数量是总权重/各服务器权重的最大公约数,为了避免一次性集中分配虚拟节点导致CPU压力突发,所以每次最多分配max_init个数的虚拟节点。
  这是这些逻辑交织在一起,看上去ngx_http_upstream_get_vnswrr函数似乎有些复杂了。
  和不带权重的模式一样,它也会在第一次分配的时候设置一个随机值位置,从随机的虚拟节点开始分配,避免“共振”现象的发生。

  除了以上特别说明的部分,其他逻辑几乎就是round-robin代码的翻版,还是非常好理解的,本文列出的源码中也给出了注释,就不再赘述了。

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

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

相关文章

`psbt` 字符串进行签名,并将签名结果以 Base64 的形式返回给客户端 magic eden

逐步解释代码的功能和行为: 1. 导入所需的模块:使用 `require` 关键字导入了一些需要用到的第三方模块,包括 `express`、`body-parser`、`ecpair`、`tiny-secp256k1`、`bitcoinjs-lib` 和 `bitcoinjs-lib/src/psbt/bip371`。 2. 创建 Express 应用程序实例:使用 `express…

【Numpy】(2)numpy对象和random模块

numpy.array对象 numpy.array 对象是 NumPy 库的核心&#xff0c;它提供了一种高效的方式来存储和操作同质数据类型的多维数组。每个 numpy.array 对象都有一系列的属性&#xff0c;这些属性提供了关于数组的重要信息。理解这些属性对于有效地使用 NumPy 和进行数据分析是非常…

【QT入门】VS2019+QT的开发环境配置

声明&#xff1a;该专栏为本人学习Qt知识点时候的笔记汇总&#xff0c;希望能给初学的朋友们一点帮助(加油&#xff01;) 往期回顾&#xff1a; 【QT入门】什么是qt&#xff0c;发展历史&#xff0c;特征&#xff0c;应用&#xff0c;QtCreator-CSDN博客【QT入门】Windows平台下…

项目三 操作学生管理数据库中的表

项目三 操作学生管理数据库中的表 1&#xff0c;使用MySQL中的常用数据类型 数据类型决定了数据的取值范围、存储方式与占用的空间大小以及能够对其进行的一组合法操作。 1.1&#xff0c;使用数值类型 1&#xff0c;MySQL 的数值类型大致可以分为两个类别&#xff1a;整数类…

论文阅读——GeoChat(cvpr2024)

GeoChat : Grounded Large Vision-Language Model for Remote Sensing 一、引言 GeoChat&#xff0c;将多模态指令调整扩展到遥感领域以训练多任务会话助理。 遥感领域缺乏多模式指令调整对话数据集。受到最近指令调优工作的启发&#xff0c;GeoChat 使用 Vicuna-v1.5和自动化…

深度学习-解读GoogleNet深度学习网络

深度学习-解读GoogleNet深度学习网络 深度学习中&#xff0c;经典网络引领一波又一波的技术革命&#xff0c;从LetNet到当前最火的GPT所用的Transformer&#xff0c;它们把AI技术不断推向高潮。2012年AlexNet大放异彩&#xff0c;它把深度学习技术引领第一个高峰&#xff0c;打…

安卓UI面试题 45-50

46. Android中View几种常见位移方式的区别? 1. offsetLeftAndRight() 和 offsetTopAndBottom() (推荐) 实现的是对view的移动 offsetLeftAndRight(int offset): 水平方向挪动View,offset为正则x轴正向移动,getLeft()和getRight()会变。 offsetTopAndBottom(int offset): …

android studio 连接mumu模拟器调试

1、打开mumu模拟器 2、在Android Studio 中 控制台 cd 到 sdk 目录下 platform-tools 文件夹&#xff0c;有一个adb.exe 可运行程序 一般指令&#xff1a; adb connect 127.0.0.1:7555 但是这个执行在window环境下可能会报错 解决方法是在 adb 之前加 ".\", 问题…

Python实战:NumPy数组与矩阵操作入门

NumPy是Python数据科学领域中不可或缺的库之一&#xff0c;它提供了一个强大的N维数组对象和一系列用于操作这些数组的函数。本文将详细介绍NumPy数组与矩阵的基础知识&#xff0c;包括数组的创建、操作、切片、索引、以及矩阵的运算等。 1. 引言 在Python数据科学领域&#…

【AI】用iOS的ML(机器学习)创建自己的AI App

用iOS的ML(机器学习)创建自己的AI App 目录 用iOS的ML(机器学习)创建自己的AI App机器学习如同迭代过程CoreML 的使用方法?软件要求硬件开始吧!!构建管道:设计和训练网络Keras 转 CoreML将模型集成到 Xcode 中结论推荐超级课程: Docker快速入门到精通Kubernetes入门到…

迷茫了!去大厂还是创业?

大家好&#xff0c;我是麦叔&#xff0c;最近我创建了一个 学习圈子 有球友在 星球 里提问。 大厂的layout岗位和小厂的硬件工程师岗位&#xff0c;该如何选择&#xff1f; 这个问题我曾经也纠结过&#xff0c;不过现在的我&#xff0c;I am awake&#xff01; 肯定是有大点大。…

GiT: Towards Generalist Vision Transformer through Universal Language Interface

GiT: Towards Generalist Vision Transformer through Universal Language Interface 相关链接&#xff1a;arxiv github 关键字&#xff1a;Generalist Vision Transformer (GiT)、Universal Language Interface、Multi-task Learning、Zero-shot Transfer、Transformer 摘要 …

小清新卡通人物404错误页面模板源码

小清新卡通人物404错误页面模板源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 小清新卡通人物404错误页面模板源码

ClickHouse:一款高效且强大的列式数据库管理系统

ClickHouse是一款开源的列式数据库管理系统&#xff0c;专为大规模数据仓库和数据分析应用而设计。它允许用户快速地存储和处理海量数据&#xff0c;同时提供了简单易用的SQL接口。本文将介绍ClickHouse的概念、技术原理以及使用案例&#xff0c;并探讨其优势和挑战。 一、引言…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:WaterFlow)

瀑布流容器&#xff0c;由“行”和“列”分割的单元格所组成&#xff0c;通过容器自身的排列规则&#xff0c;将不同大小的“项目”自上而下&#xff0c;如瀑布般紧密布局。 说明&#xff1a; 该组件从API Version 9 开始支持。后续版本如有新增内容&#xff0c;则采用上角标单…

IOS面试题object-c 111-115

111. 简述MRC (手动引用计数)和 ARC( 自动引用计数) ) ?1 1 、 MRC : alloc , retain , release , retainCount,autorelease,dealloc2 2 、 ARC :  ARC 是 LLVM 和 Runtime 协作的结果  ARC 禁止手动调用 retain,release,retainCount,autorelease 关键字  ARC …

Django验证码(二)

一、生成图片 1.1、说明 通过pillow模板库生成图片,步骤如下 安装pillow模板建立 生成验证码内容 方法建立 生成验证码颜色 方法建立 生成验证码 方法1.2、需要安装 Pillow 库 pip install Pillow==9.3.01.3、生成验证码内容 import randomdef random_str(length=4):"…

html中如何让网页禁用右键禁止查看源代码

在网页中&#xff0c;辛辛苦苦写的文章&#xff0c;被别人复制粘贴给盗用去另很多站长感到非常无奈&#xff0c;通常大家复制都会使用选取右键复制&#xff0c;或CTRLC等方式&#xff0c;下面介绍几种禁止鼠标右键代码&#xff0c;可减少网页上文章被抄袭的几率&#xff0c;当然…

17个工作必备的Python自动化代码分享(下篇)

10.网络自动化 10.1检查网站状态 # Python script to check the status of a website import requests def check_website_status(url): response requests.get(url) if response.status_code 200: # Your code here to handle a successful response else: # Your code he…

Linux学习方法-框架学习法——Linux系统框架

配套视频学习链接&#xff1a;https://www.bilibili.com/video/BV1HE411w7by?p2&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux系统框架(从裸机到OS) Linux可看成是一个大软件/大程序 应用和驱动 内核态和用户态 Linux的文件系统 Linux初学者首先要搞清楚三…