哲学原理Nginx限速模块初探

by admin on 2018年11月19日

Cargo支持少数种本地部署的主意,分别吗standalone模式以及existing模式。在stsndalone模式遭遇,Cargo会从web容器的设置目录复制一份配置到用户指定的目录,然后在是基础及部署下,每次又构建的时节,这个目录都见面叫清空,所有配置为重复转。

试行2——burst允许缓存处理突发请求

试行1我们看,我们欠日外发送了大气呼吁,Nginx按照毫秒级精度统计,超出限制的呼吁直接拒绝。这当骨子里状况被不休过于苛刻,真实网络环境遭到要到来不是匀速的,很可能产生求“突发”的情况,也就是“一股子一股子”的。Nginx考虑到了这种情形,可以经burst首要字被对突如其来请求的缓存处理,而不是直接拒绝。

来拘禁咱们的配备:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4;
    }
}
...

咱们投入了burst=4,意思是每个key(此处是每个IP)最多允许4独突发请求的来临。如果单个IP在10ms内发送6单请求,结果会什么呢?

# 单个IP 10ms内发送6个请求,设置burst
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
end, total time cost: 2437 ms

相比之下实验1改为功数增加了4单,这个我们安的burst数目是同的。具体处理流程是:1独请求被当下处理,4个请求让置于burst队列里,另外一个央于驳回。通过burst参数,我们让Nginx限流具备了缓存处理突发流量之力。

不过要小心:burst的来意是深受多余的伸手可以先放到行列里,慢慢处理。如果非加nodelay参数,队列里的要不见面应声处理,而是按rate设置的快慢,以毫秒级精确的快慢渐渐处理。

然后运行命令如下:

试验3——nodelay降低排队时

试2饱受我们看看,通过设置burst参数,我们得以允许Nginx缓存处理得水准之爆发,多余的恳求可以先放到行列里,慢慢处理,这起及了平滑流量的意。但是如果队列设置的比较深,请求排队的时便见面较丰富,用户角度看来就是RT变长了,这对准用户大不友好。有啊解决办法呢?nodelay参数允许请求在排队的下就是立刻叫处理,也就是说要请求能上burst队列,就会立刻为后台worker处理,请留意,这象征burst设置了nodelay时,系统瞬间底QPS可能会见过rate设置的阈值。nodelay参数要跟burst一起利用才有图。

累实验2的配置,我们进入nodelay选项:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
    }
}
...

单个IP 10ms内并发发送6个请求,结果如下:

# 单个IP 10ms内发送6个请求
   实验3, 设置burst和nodelay       |  实验2, 只设置burst
send 6 requests, time cost: 4 ms |  time cost: 2 ms
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 503 ...
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 503 ...                 |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
total time cost: 465 ms          |  total time cost: 2437 ms

暨实验2比照,请求成功率没变化,但是整体耗时变短了。这怎么解释吗?实验2挨,有4只请求被平放burst队列当中,工作经过每隔500ms(rate=2r/s)取一个求进行处理,最后一个央而排队2s才见面于处理;实验3遇,请求放入队列跟实验2是一致的,但不同之是,队列中的请求而有了让处理的身份,所以实验3面临的5独请求可以视为同时开班于处理的,花费时间本变短了。

唯独要留意,虽然设置burst和nodelay能够降低突发请求的拍卖时,但是长期来拘禁并无会见增高吞吐量的上限,长期吞吐量的上限是出于rate决定的,因为nodelay只能保证burst的要被立刻处理,但Nginx会限制队列元素释放的速,就像是限制了使牌桶中令牌产生的快慢。

观此间您也许会见问,加入了nodelay参数后的限速算法,到底算是哪一个“桶”,是漏桶算法还是令牌桶算法?当然还算是漏桶算法。考虑同栽情景,令牌桶算法的token为耗尽时会见怎么开为?由于它们有一个请队列,所以会见把接下去的请求缓存下来,缓存多少受限于行大小。但此时缓存这些请求还有意思也?如果server已经过载,缓存队列越来越丰富,RT越来越大,即使过了老大遥远请求被拍卖了,对用户来说吧尚无什么价了。所以当token不足够用时,最神之做法尽管是一直拒绝用户的恳求,这虽改为了漏桶算法,哈哈~

下一场运行以下命令:

摘要: Nginx限速模块分为哪几种?按请求速率限速的burst和nodelay参数是什么意思?漏桶算法和令牌桶算法究竟有啊两样?本文将带来你一样探究竟。我们会由此有些简的言传身教展示Nginx限速模块是什么样行事之,然后成代码讲解其偷的算法和规律。

或指定端口:

结尾

正文主要讲解了Nginx按请求速率限速模块的用法及原理,其中burst和nodelay参数是善招误解的,虽然可经过burst允许缓存处理突发请求,结合nodelay能够降低突发请求的拍卖时,但是长期来拘禁她们并无会见加强吞吐量的上限,长期吞吐量的上限是由于rate决定的。需要特别注意的凡,burst设置了nodelay时,系统瞬间底QPS可能会见过rate设置的阈值。

正文只是对Nginx管中窥豹,更多关于Nginx介绍的章,可参看Tengine团队写的Nginx开发从入门到精通。

  1. [ERROR]Failed to execute goal org.codehaus.cargo:cargo-maven2-plugin:1.6.1:redeploy (default-cli) on project manage-admin:Executiondefault-cli of goal org.codehaus.cargo:cargo-maven2-plugin:1.6.1:redeploy faile
  2. d:Failed to redeploy [H:\workspace\QianduManage\manage-admin\target\manage-admin.war]:The username you provided is not allowed to use the text-based TomcatManager(error 403):Server returned HTTP response code
  3. :403for URL: http://localhost:8080/manager/text/list -> [Help 1]
  4. [ERROR]
  5. [ERROR]To see the full stack trace of the errors, re-run Maven with the -e switch.
  6. [ERROR]Re-run Mavenusing the -X switch to enable full debug logging.
  7. [ERROR]
  8. [ERROR]For more information about the errors and possible solutions, please read the following articles:
  9. [ERROR][Help1] http://cwiki.apache.org/confluence/display/MAVEN/PluginExecutionException

漏桶算法是怎样兑现的

漏桶算法的兑现呢于我们想象的简短,其基本是立等同行公式excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000,这样代码的意是:excess表示目前key上遗留的要数,本次遗留的请求数
= 上次留的乞求数 – 预设速率 X 过去的时刻 +
1
。这个1代表目前之要,由于Nginx内部表示用单位压缩了1000倍,所以1独请求而转换成1000。

excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
if (excess < 0) 
    excess = 0;
if ((ngx_uint_t) excess > limit->burst)
    return NGX_BUSY; // 超过了突发门限,拒绝
if (account) { // 是否是最后一条规则
    lr->excess = excess;    
    lr->last = now;    
    return NGX_OK; // 未超过限制,通过
}
...
return NGX_AGAIN; // 6. 执行下一条限流规则

上述代码受限算出当前key上遗留的恳求数,如果超过了burst,就直接拒绝;由于Nginx允许多长条限速规则以由作用,如果既是最终一漫长规则,则允许通过,否则执行下同样修规则。

  1. systemctl stop firewalld

单个key相关的burst队排于哪

无单个key相关的burst队列。上面代码中我们看出当到达最终一长条规则时,只要excess<limit->burst限速模块就见面回来NGX_OK,并不曾把多余请求放入队列的操作,这是因Nginx是根据timer来管理要的,当限速模块返回NGX_OK时,调度函数会计算一个推处理的时间,同时将这个请放入到共享的timer队列中(一蔸按等待时自小至充分排序的吉祥黑树)。

ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    ...
    for (n = 0; n < lrcf->limits.nelts; n++) {
        ...
        ngx_shmtx_lock(&ctx->shpool->mutex);// 获取锁
        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, // 执行漏桶算法
                                       (n == lrcf->limits.nelts - 1));
        ngx_shmtx_unlock(&ctx->shpool->mutex);// 释放锁
        ...
        if (rc != NGX_AGAIN)
            break;
    }
    ...
    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);// 计算当前请求需要的延迟时间
    if (!delay) {
        return NGX_DECLINED;// 不需要延迟,交给后续的handler进行处理
    }
    ...
    ngx_add_timer(r->connection->write, delay);// 否则将请求放到定时器队列里
    return NGX_AGAIN; // the request has been successfully processed, the request must be suspended until some event. http://www.nginxguts.com/2011/01/phases/
}

咱俩来看ngx_http_limit_req_handler()调用了函数ngx_http_limit_req_lookup(),并基于其回来回值决定如何操作:或是拒绝,或是交给下一个handler处理,或是将请求放入定期器队列。当限速规则都经过后,该hanlder通过调用函数ngx_http_limit_req_account()汲取当前请需要的延迟时间,如果无欲延期,就将呼吁提交后续之handler进行拍卖,否则用请放到定时器队列里。注意这个定时器队列是共享的,并从未也单身的key(比如,每个IP地址)设置队列。关于handler模块背景知识之牵线,可参照Tengine团队做之Nginx开发从入门到精通

关于本请求速率限速的法则教学,可参考Rate Limiting with NGINX and NGINX
Plus,关于源码更详细的剖析只是参看ngx_http_limit_req_module
源码分析以及y123456yz的Nginx源码分析的git项目

亟待拿上述Configuration子元素type的值为existing,而对此的home子元素表示现有的Web容器目录。运行后,可在Tomcat的webapps子目录看到给布置之Maven项目。

遵请求速率限速

随请求速率限速大凡依赖限制单个IP(或者其他的key)发送请求的速率,超出指定速率后,Nginx将直拒绝再多的乞求。采用leaky
bucket
算法实现。为深切了解这个模块,我们先从实验现象说自。开始之前我们事先简单介绍一下欠模块的部署方式,以下面的布局为条例:

http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=mylimit burst=4 nodelay;
        }

使用limit_req_zone根本字,我们定义了一个叫吧mylimit大小也10MB的共享内存区域(zone),用来存放在限速相关的统计信息,限速的key价为二进制的IP地址($binary_remote_addr),限速上限(rate)为2r/s;接着我们运用limit_req主要字用上述规则作用及/search/上。burstnodelay的作用稍后解释。

运上述规则,对于/search/目录的看,单个IP的访问速度被界定于了2请求/秒,超过此界定的访问将直接为Nginx拒绝。

 

尝试1——毫秒级统计

俺们出如下配置:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit;
    }
}
...

上述规则限制了每个IP访问的快吗2r/s,并以欠规则作用被与目录。如果单个IP在特别紧缺的时空内连发发送多独请求,结果碰头怎么为?

# 单个IP 10ms内并发发送6个请求
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
end, total time cost: 461 ms

咱使用单个IP在10ms内发并发送了6独请求,只出1独成功,剩下的5个都为拒。我们安的速度是2r/s,为什么就生1只成功为,是勿是Nginx限制错了?当然不是,是盖Nginx的限流统计是冲毫秒的,我们设置的速度是2r/s,转换一下便是500ms内单个IP只同意通过1单请求,从501ms开始才同意通过第二只请求。

哲学原理 1

瞩目,若要布局于长途机器,则需要注意以下几点:

中心算法

每当追Nginx限速模块之前,我们先行来探视网络传输中不时因此有限只的流量控制算法:漏桶算法和令牌桶算法。这点儿单“桶”到底出什么异同呢?

 

Nginx限速模块

Nginx主要出星星点点栽限速措施:按连接数限速(ngx_http_limit_conn_module)、按请求速率限速(ngx_http_limit_req_module)。我们重点讲解按请求速率限速。

1、原理

源码剖析

透过地方的示范,我们队要限速模块出矣一定的认识,现在我们深切解析代码实现。按请求速率限流模块ngx_http_limit_req_module代码位于[src/http/modules/ngx_http_limit_req_module.c
](https://github.com/nginx/nginx/blob/master/src/http/modules/ngx_http_limit_req_module.c),900多执行代码可谓短小精悍。相关代码有点儿个基本数据结构:

  1. 开门红黑树:通过红黑树记录每个节点(按照声明时指定的key)的统计信息,方便寻找;
  2. LRU队列:将吉黑树上的节点按照最近看时排序,时间即的放在队列头部,以便使LRU队列淘汰旧的节点,避免内存溢出。

眼看有限个举足轻重目标存储在ngx_http_limit_req_shctx_t中:

typedef struct {
    ngx_rbtree_t                  rbtree; /* red-black tree */
    ngx_rbtree_node_t             sentinel; /* the sentinel node of red-black tree */
    ngx_queue_t                   queue; /* used to expire info(LRU algorithm) */
} ngx_http_limit_req_shctx_t;

个中除rbtree和queue之外,还有一个号称sentinel的变量,这个变量用作红黑树的NIL节点。

拖欠模块的主干逻辑在函数ngx_http_limit_req_lookup()被,这个函数主要流程是什么样也?对于各级一个求:

  1. 自从根节点开始查找红黑树,找到key对应的节点;
  2. 找到后修改该点在LRU队列中之职位,表示该点最近叫访问了;
  3. 推行漏桶算法;
  4. 尚无找到时因LRU淘汰,腾出空间;
  5. 变迁并插入新的红黑树节点;
  6. 实践下一样长长的限流规则。

流程很清楚,但是代码中拉到红黑树、LRU队列顶高等数据结构,是未是会写得十分复杂?好以Nginx作者功力深厚,代码写得简洁易掌握,哈哈~

// 漏桶算法核心流程
ngx_http_limit_req_lookup(...){
  while (node != sentinel) {
      // search rbtree
    if (hash < node->key) { node = node->left; continue;} // 1. 从根节点开始查找红黑树
    if (hash > node->key) { node = node->right; continue;}
    rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
    if (rc == 0) {// found
      ngx_queue_remove(&lr->queue); // 2. 修改该点在LRU队列中的位置,表示该点最近被访问过
      ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
      ms = (ngx_msec_int_t) (now - lr->last);
      excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
      if (excess < 0) 
          excess = 0;
      if ((ngx_uint_t) excess > limit->burst)
          return NGX_BUSY; // 超过了突发门限,拒绝
      if (account) {// 是否是最后一条规则
        lr->excess = excess;    
        lr->last = now;    
        return NGX_OK; // 未超过限制,通过
      }
      ...
      return NGX_AGAIN; // 6. 执行下一条限流规则
    }
    node = (rc < 0) ? node->left : node->right; // 1
  } // while
  ...
  // not found
  ngx_http_limit_req_expire(ctx, 1); // 4. 根据LRU淘汰,腾出空间
  node = ngx_slab_alloc_locked(ctx->shpool, size); // 5. 生成新的红黑树节点
  ngx_rbtree_insert(&ctx->sh->rbtree, node);// 5. 插入该节点,重新平衡红黑树
  ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
  if (account) {    
    lr->last = now; 
    lr->count = 0;
    return NGX_OK;
  }
  ...
  return NGX_AGAIN; // 6. 执行下一条限流规则
}

代码有三栽回到值,它们的意是:

  • NGX_BUSY 超过了爆发门限,拒绝
  • NGX_OK 未过限定,通过
  • NGX_AGAIN 未超限定,但是还有规则不执行,需实施下同样长达限流规则

上述代码不难理解,但咱还有几只问题:

  1. LRU是什么兑现的?
  2. 漏桶算法是如何落实的?
  3. 每个key相关的burst队排于哪?

 

按照连接数限速

遵照连接数限速大凡依赖限制单个IP(或者其他的key)同时提倡的连数,超出这个范围后,Nginx将一直拒绝再多之连日。这个模块的布置于好明,详见ngx_http_limit_conn_module官方文档。

3、远程机器的Tomcat允许任何机器登录manager页面

漏桶算法(leaky bucket)

漏桶算法(leaky
bucket)算法思想如图所示:

哲学原理 2

一个像的解说是:

  • 回(请求)从上边倒入水桶,从水桶下方流出(被处理);
  • 不及流出的巡在水桶中(缓冲),以固定速率流出;
  • 水桶满后和漫起(丢弃)。

这个算法的主导是:缓存请求、匀速处理、多余的请直接抛。

2、部署到远程Web容器

Nginx限速模块分为哪几种?按请求速率限速的burst和nodelay参数是什么意思?漏桶算法和令牌桶算法究竟出啊两样?本文将拉动你平探究竟。我们会通过有些略的言传身教展示Nginx限速模块是什么样行事之,然后成代码讲解其偷的算法和法则。

倘以existing模式遭遇,用户需要指定现有的web容器配置目录,然后Cargo会直接采用这些安排并以应用部署到其相应之职位。

叫牌桶算法(token bucket)

令牌桶(token
bucket)算法思想如图所示:

哲学原理 3

算法思想是:

  • 使得牌以固定速率产生,并缓存到叫牌桶中;
  • 令牌桶放满时,多余的令牌被丢弃;
  • 呼吁而吃相当比例之令牌才会给拍卖;
  • 令牌不够时,请求于缓存。

对待漏桶算法,令牌桶算法不同之处在于它不只有相同单纯“桶”,还发生只队,这个桶是为此来存放在令牌的,队列才是因此来存放请求的。

自从图上吧,漏桶和令牌桶算法最明确的界别就是是否允许突发流量(burst)的拍卖,漏桶算法能够强行限制数量的实时传输(处理)速率,对突如其来流量不举行额外处理;而令牌桶算法能够当限数量的平均传输速率的以同意某种程度的突发传输。

Nginx按请求速率限速模块使用的是漏桶算法,即会强行保证请求的实时处理速度不会见过设置的阈值。

 

LRU是什么实现的

LRU算法的兑现好简单,如果一个节点被聘了,那么尽管拿它们换到队的头,当空间不足需要淘汰节点时,就选出队列尾部的节点淘汰掉,主要反映于如下代码中:

ngx_queue_remove(&lr->queue); // 2. 修改该点在LRU队列中的位置,表示该点最近被访问过
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
...
ngx_http_limit_req_expire(ctx, 1); // 4. 根据LRU淘汰,腾出空间

4、plugin/configuration/configuration/properties需要安排cargo.hostname域名,否则默认localhost(无论cargo.tomcat.manager.url如何布置)

待以Tomcat目录的conf/Catalina/localhost/下增产文件:

默认情况下,只有org.apache.maven.plugins和org.codehaus.mojo两个gounpId下之插件才支撑简化的命令行应用,即可运行mvn
help:system。但mvn jetty:run就坏。

自为解笔记(Wiz)

  1. mvn cargo:start

 

哲学原理 4

 

亚、使用jetty-maven-plugin进行测试

  1. <role rolename="tomcat"/>
  2. <role rolename="role1"/>
  3. <role rolename="admin"/>
  4. <role rolename="manager-gui"/>
  5. <role rolename="manager-script"/>
  6. <role rolename="manager"/>
  7. <user username="admin" password="admin123" roles="admin,manager,manager-script,manager-gui"/>

哲学原理 5

注意,与jetty一样,cargo-maven2-plugin的groupId是org.codehaus.cargo,不属于官的星星点点单Maven插件groupId,若用简化的命令行则需要以settings.xml中pluginGroup元素中配备。

顶尖POM中设定的价值吗${project.artifactId}-${project.version}.war。

3、启动jetty-maven-plugin

  1. mvn jetty:run

设若standalone模式之布局样例:

1、显式指定Web项目打包方式啊war:

 

 

<plugin>
    <groupId>org.codehaus.cargo</groupId>
    <artifactId>cargo-maven2-plugin</artifactId>
    <version>1.6.1</version>
    <configuration>
        <container>
            <containerId>tomcat8x</containerId>
            <type>remote</type>
        </container>
        <configuration>
            <type>runtime</type>
            <properties>
                <cargo.hostname>10.110.2.161</cargo.hostname>
                <cargo.remote.username>admin</cargo.remote.username>
                <cargo.remote.password>admin123</cargo.remote.password>
                <cargo.tomcat.manager.url>http://10.110.2.161:8080/manager</cargo.tomcat.manager.url>
            </properties>
        </configuration>
    </configuration>
</plugin>

哲学原理 6

 

  1. mvn cargo:redeploy

哲学原理 7

 

据悉“约定大于配置”的规则,Web项目之类及资源文件默认位置为src/main/java和src/main/resources,测试接近和测试资源文件默认位置src/test/java和src/test/resources,Web资源目录在src/main/webapp。

除了吃Cargo直接管理当地Web容器外,还好于Cargo部署应用至远程的着运作的Web容器,前提是具有该容器的应和权限。

 

tomcat中的用户权限可参考如下配置: 

倘红帽Linus系列使用如下命令:

manager.xml,内容如下:

悬停测试运行,可Ctrl+C即可。

哲学原理 8

 

下一场可运行:

哲学原理 9

 

 

2、默认目录

webappConfig元素下之contextPath表示项目布局后的上下文路径,上述配置/test表示用户可以经过http://hostname:port/test/访问该应用。

 

1、远程机器防火墙要关

哲学原理 10

其三、使用Cargo实现自动化部署

 

existing模式:

 

哲学原理 11

以能以命令执行直接运行mvn jetty:run,用户用配备settings.xml如下:

动cargo,只待同修简单的maven命令,就可构建项目并部署及Web容器中,进行功能测试。

布局测试环境或最终产品环境,需要简单的war包,需要安排finalName元素。

人情的Web测试方法要求编译、测试、打包及配置,这频繁吃过多年华,jetty-maven-plugin能够帮助我们节省时间,它能够周期性地检查项目内容,发现变更后自动更新到放的Jetty
Web容器中。即,帮咱省了打包和安排的步骤。

Cargo默认Web容器监听8080端口,也足以通过如下配置进行改动:

 

Maven项目布局没有War包中之lib目录,因为依靠还配备在POM中,Maven在就此War方式打包的时光会依据POM配置从本土仓库复制相应的Jar文件。

 

 

平等、Web项目组织

2、远程机器的Tomcat要部署manager用户,且如果生manager-script角色

 

 

 

 

 

 

2、配置

1、部署至地方Web容器

参考上述tomcat中之用户权限配置,否则可能会见出如下错误:

若容器已经配备了目前采取,Cargo会先将该卸载,然后重新部署。

cargo-maven2-plugin和jetty-maven-plugin的成效看起相似,但是目的不同。jetty-maven-plugin主要用来助日常的飞快开同测试,而cargo-maven2-plugin主要服务为自动化部署。

  1. mvn jetty:run -Djetty.port=9999
  1. <Contextprivileged="true"antiResourceLocking="false"docBase="${catalina.home}/webapps/manager">
  2. <ValveclassName="org.apache.catalina.valves.RemoteAddrValve"allow="^.*$"/>
  3. </Context>

3、war包名称——finalName元素配置

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图