者|邹晟 去哪儿网基础平台技术专家
近几年,云原生和容器技术非常火爆,且日趋成熟,众多企业慢慢开始容器化建设,并在云原生技术方向上不断的探索和实践。基于这个大的趋势, 2020 年底 Qunar 也向云原生迈出了第一步——容器化。
云原生是一系列可以为业务赋能的技术架构准则,遵循它可以使应用具有扩展性、伸缩性、移植性、韧性等特点。云原生也是下一代技术栈的必选项,它可以让业务更敏捷。通过实践 DevOps、微服务、容器化、可观测性、反脆弱性(chaos engineering)、ServiceMesh、Serverless 等云原生技术栈,我们便可以享受到云原生带来的技术红利。
一项新技术要在企业内部落地从来都不是一蹴而就的,Qunar 的容器化落地也同样如此。Qunar 的容器后落地主要经历了 4 个时间节点:
2014 - 2015:
业务线同学开始尝试通过 Docker、Docker-Compose 来解决联调环境搭建困难的问题,不过由于 Docker-Compose 的编排能力有限、无法解决真实的环境问题,因此容器化最后也没有推行起来。
2015 - 2017:
ops 团队把为了提高 ELK 集群的运维效率,把 ES 集群迁移到了 Mesos 平台上。后来随着 K8s 生态的成熟,把 ES 集群从 Mesos 迁移到了 K8s 平台,运维效率得到了进一步的提升。
2018 - 2019:
在业务需求不断增加的过程中,业务对测试环境的交付速度和质量有了更高的要求,为了解决 MySQL 的交付效率问题( 并发量大时,网络 IO 成为了瓶颈,导致单个实例交付时长在分钟级),为了解这个问题,我们把 MySQL 容器化,通过 Docker on host 的模式可以在 10 秒之内就可以交付一个 MySQL 实例。
2020 - 2021:
云原生技术已经非常成熟了,Qunar 也决定通过拥抱云原生来为业务增加势能。在各个团队齐心协力的努力下,300+ 的 P1、P2 应用已经完成了容器化,并且计划在 2021 年年底全部业务应用实现容器化。
Qunar 在做容器化过程中,各个系统 Portal 平台、中间件、ops 基础设施、监控等都做了相应的适配改造,改造后的架构矩阵如下图所示。
Portal:Qunar 的 PaaS 平台入口,提供 CI/CD 能力、资源管理、自助运维、应用画像、应用授权(db 授权、支付授权、应用间授权)等功能。
运维工具:提供应用的可观测性工具, 包括 watcher(监控和报警)、bistoury (Java 应用在线 Debug)、qtrace(tracing 系统)、loki/elk(提供实时日志/离线日志查看)。
中间件:应用用到的所有中间件,mq、配置中心、分布式调度系统 qschedule、dubbo 、mysql sdk 等。
虚拟化集群:底层的 K8s 和 OpenStack 集群。
Noah:测试环境管理平台,支持应用 KVM/容器混合部署。
主要改造点:
应用画像: 把应用相关的运行时配置、白名单配置、发布参数等收敛到一起,为容器发布提供统一的声明式配置。
授权系统: 应用所有的授权操作都通过一个入口进行,并实现自动化的授权。
K8s 多集群方案: 通过调研对比,KubeSphere 对运维优化、压测评估后也满足我们对性能的要求,最终我们选取了 KubeSphere 作为多集群方案。
改造关注点:由于容器化后,IP 经常变化是常态,所以各个公共组件和中间件要适配和接受这种变化。
为了帮助业务快速平滑地迁移到容器,我们制定了一些规范和自动化测试验证等操作来实现这个目标。
容器化的前置条件: 应用无状态、不存在 post_offline hook(服务下线后执行的脚本)、check_url 中不存在预热操作。
测试环境验证: 自动升级 SDK、自动迁移。我们会在编译阶段帮助业务自动升级和更改 pom 文件来完成 SDK 的升级,并在测试环境部署和验证,如果升级失败会通知用户并提示。
线上验证: 第一步线上发布,但不接线上流量,然后通过自动化测试验证,验证通过后接入线上流量。
线上 KVM 与容器混部署:保险起见,线上的容器和 KVM 会同时在线一段时间,等验证期过后再逐步下线 KVM。
线上全量发布: 确认服务没问题后,下线 KVM。
观察: 观察一段时间,如果没有问题则回收 KVM。
KVM 场景中 hook 脚本使用场景介绍:
preStart hook : 用户在这个脚本中会自定义命令,比如环境准备。
preOnline hook:用户会定义一些数据预热操作等,这个动作需要在应用 checkurl 通过并且接入流量前执行。
问题点:
K8s 原生只提供了 preStop、postStart 2 种 hook, 它们的执行时机没有满足上述 2 个 KVM 场景下业务用到的 hook。
分析与解决过程:
preStart hook:在 entrypoint 中注入 preStart hook 阶段,容器启动过程中发现有自定义的 preStart 脚本则执行该脚本,至于这个脚本的位置目前规范是定义在代码指定目录下。
preOnline hook:由于 preOnline 脚本执行时机是在应用 checkurl 通过后,而应用容器是单进程,所以在应用容器中执行这个是行不通的。而 postStart hook 的设计就是异步的,与应用容器的启动也是解耦的, 所以我们初步的方案选择了 postStart hook 做这个事情。实施方案是 postStart hook 执行后会不断轮询应用的健康状态,如果健康检测 checkurl 通过了, 则执行 preOnline 脚本。脚本成功后则进行上线操作, 即在应用目录下创建 healthcheck.html 文件,OpenResty 和中间件发现这个文件后就会把流量接入到这个实例中。
按照上面的方案,Pod 的组成设计如下:
场景介绍:
在容器发布过程中如果应用启动失败,我们通过 K8s API 是拿不到实时的标准输入输出流,只能等到发布设置的超时阈值,这个过程中发布人员心里是很焦急的,因为不确定发生了什么。如下图所示,部署过程中应用的更新工作流中什么都看不到。
问题点:
K8s API 为什么拿不到标准输入输出?
分析与解决过程:
通过 kubectl logs 查看当时的 Pod 日志,什么都没有拿到,超时时间过后才拿到。说明问题不在程序本身,而是在 K8s 的机制上;
查看 postStart Hook 的相关文档,有一段介绍提到了 postHook 如果执行时间长或者 hang 住,容器的状态也会 hang 住,不会进入 running 状态, 看到这条信息,大概猜测到罪魁祸首就是这个 postStart hook 了。
基于上面的猜测,把 postStart hook 去掉后测试,应用容器的标准输入可以实时拿到了。
找到问题后,解决方法也就简单了,把 postStart hook 中实现的功能放到 Sidecar 中就可以解决。至于 Sidecar 如何在应用容器的目录中创建 healthcheck.html 文件,就需要用到共享卷了。新的方案设计如下:
使用上述方案后,发布流程的标准输入输出、自定义 hook 脚本的输出、Pod 事件等都是实时可见的了, 发布过程更透明了。
场景介绍:
我们的应用是多机房多集群部署的,当一个应用的新版本发布时,由于应用的实例数较多,有 50+ 个并发从 harbor 拉取镜像时,其中一些任务收到了镜像拉取超时的报错信息,进而导致整个发布任务失败。超时时间是 kubelet 默认设置的 1 分钟。
分析与解决:
通过排查最终确认是 harbor 在并发拉取镜像时存在性能问题,我们采取的优化方案是通用的 p2p 方案,DragonFly + Harbor。
场景介绍:
应用发布过程中调用授权接口失败,K8s 的自愈机制会不断重建容器并重新授权,并发量比较大,最终把授权服务拖垮。
我们的容器授权方案如下:
Pod init 容器启动时进行调研授权接口进行授权操作,包括 ACL 和 mysql 的白名单。
容器销毁时会执行 Sidecar 容器的 preStop hook 中执行权限回收操作。
问题点:
ACL 授权接口涉及到了防火墙,QPS 比较低,大量容器进行 ACL 授权时把服务拖垮 。
分析与解决:
为了解决上述的问题,限量和降低授权接口调用次数是有效的解决方式。我们采取了下面几个措施:
init 容器中的重试次数限制为 1 次。
授权接口按应用和 IP 限流, 超过 3 次则直接返回失败,不会再进行授权操作。
ACL 中涉及的一些通用的端口,我们统一做了白名单,应用无需再进行授权操作。
KVM 场景 Debug 介绍:
在开发 Java 应用的过程中,通过远程 Debug 可以快速排查定位问题,因此是开发人员必不可少的一个功能。Debug 具体流程: 开发人员在 Noah 环境管理平台的界面点击开启 Debug, Noah 会自动为该 Java 应用配置上 Debug 选项,-Xdebug -Xrunjdwp: transport=dt_socket, server=y, suspend=n, address=127.0.0.1:50005,并重启该 Java 应用,之后开发人员就可以在 IDE 中配置远程 Debug 并进入调试模式了。
容器场景的 Debug 方案:
测试环境的 Java 应用默认开启 Debug 模式,这样也避免了更改 Debug 重建 Pod 的过程,速度从 KVM 的分钟级到现在的秒级。当用户想开启 Debug 时,Noah 会调用 K8s exec 接口执行 socat 相关命令进行端口映射转发,让开发人员可以通过 socat 开的代理连接到 Java 应用的 Debug 端口。
问题点:
容器场景下在用户 Debug 过程中,当请求走到了设置的断点后,Debug 功能失效。
分析与解决过程:
复现容器场景下 Debug,观察该 Pod 的各项指标,发现 Debug 功能失效的时候系统收到了一个 liveness probe failed,kill pod 的事件。根据这个事件可以判断出当时 liveness check 失败,应用容器才被 kill 的,应用容器重启代理进程也就随之消失了,Debug 也就失效了。
关于 Debug 过程 checkurl 为什么失败的问题,得到的答案是 Debug 时当请求走到断点时,整个 JVM 是 hang 住的,这个时候任何请求过来也会被 hang 住,当然也包括 checkurl,于是我们也特地在 KVM 场景和容器场景分布做了测试,结果也确实是这样的。
临时解决方案是把断点的阻断级别改为线程级的,这样就不会阻断 checkurl 了, idea 中默认的选项是 Suspend All,改为 Suspend Thread 即可。不过这个也不是最优解,因为这个需要用户手工配置阻断级别,有认知学习成本。
回到最初的问题上,为什么容器场景下遇到这个问题,而 KVM 没有,主要是因为容器场景 K8s 提供了自愈能力,K8s 会定时执行 liveness check, 当失败次数达到指定的阈值时,K8s 会 kill 掉容器并重新拉起一个新的容器。
那我们只好从 K8s 的 liveness 探针上着手了,探针默认支持 exec、tcp 、httpGet 3 种模式,当前使用的是 httpGet,这种方式只支持一个 url, 无法满足这个场景需求。经过组内讨论, 最后大家决定用这个表达式 (checkurl == 200) || (socat process && java process alive) 在作为应用的 liveness 检测方式,当 Debug 走到断点的时候, 应用容器就不会阻断了, 完美的解决了这个问题。
以上就是我们落地容器化过程中遇到的几个问题与我们的解决思路。其中很重要的一点是从 KVM 迁移到容器时需要考虑用户的使用习惯、历史功能兼容等要点,要做好兼容和取舍,只有这样容器化落地才会更顺畅。
多集群稳定性治理
让可观测性数据更全面、覆盖度更广,进而完善我们的 APM 系统,提升排查问题效率。
通过实施混沌工程来验证、发现和消除容器化场景的稳定性盲区。
提高资源利用率
根据业务指标实现弹性扩缩容。
根据应用的历史数据智能的调整 requests。
ServiceMesh 方案落地
本文由博客一文多发平台 OpenWrite 发布!
、Tomcat 的缺省端口是多少,怎么修改?
1)找到 Tomcat 目录下的 conf 文件夹
2)进入 conf 文件夹里面找到 server.xml 文件
3)打开 server.xml 文件
4)在 server.xml 文件里面找到下列信息
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" uriEncoding="utf-8"/>
port="8080"改成你想要的端口
2、tomcat 有哪几种 Connector 运行模式(优化)?
bio:传统的 Java I/O 操作,同步且阻塞 IO。maxThreads=”150”//Tomcat 使用线程来处理接收的每个请求。这个值表示Tomcat 可创建的最大的线程数。默认值 200。可以根据机器的时期性能和内存大小调整,一般可以在 400-500。最大可以在 800 左右。minSpareThreads=”25”—Tomcat 初始化时创建的线程数。默认值 4。如果当前没有空闲线程,且没有超过 maxThreads,一次性创建的空闲线程数量。Tomcat 初始化时创建的线程数量也由此值设置。maxSpareThreads=”75”–一旦创建的线程超过这个值,Tomcat 就会关闭不再需要的 socket 线程。默认值 50。一旦创建的线程超过此数值,Tomcat 会关闭不再需要的线程。线程数可以大致上用 “同时在线人数每秒用户操作次数系统平均操作时间” 来计算。acceptCount=”100”—-指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。默认值10。如果当前可用线程数为 0,则将请求放入处理队列中。这个值限定了请求队列的大小,超过这个数值的请求将不予处理。connectionTimeout=”20000” –网络连接超时,默认值 20000,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为 30000 毫秒。
nio:JDK1.4 开始支持,同步阻塞或同步非阻塞 IO。指定使用 NIO 模型来接受 HTTP 请求 protocol=”org.apache.coyote.http11.Http11NioProtocol” 指定使用 NIO 模型来接受 HTTP 请求。默认是 BlockingIO,配置为 protocol=”HTTP/1.1” acceptorThreadCount=”2” 使用 NIO 模型时接收线程的数目aio(nio.2):JDK7 开始支持,异步非阻塞 IO。
apr:Tomcat 将于 JNI 的形式调用 Apache HTTP 服务器的核心动态链接库来处理文件读取或网络传输操作,从而大大地 提高 Tomcat 对静态文件的处理性能。
<!--
<Connector connectionTimeout="20000" port="8000"
protocol="HTTP/1.1" redirectPort="8443" uriEncoding="utf-8"/>
-->
<!-- protocol 启用 nio 模式,(tomcat8 默认使用的是 nio)(apr 模式利用系统级异步 io) -->
<!-- minProcessors 最小空闲连接线程数-->
<!-- maxProcessors 最大连接线程数-->
<!-- acceptCount 允许的最大连接数,应大于等于 maxProcessors-->
<!-- enableLookups 如果为 true,requst.getRemoteHost 会执行 DNS 查找,反向解析 ip 对应域名或主机名-->
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioPro
其他配置 maxHttpHeaderSize="8192" http 请求头信息的最大程度,超过此长度的部分不予处理。一般 8K。URIEncoding="UTF-8" 指定 Tomcat 容器的 URL 编码格式。disableUploadTimeout="true" 上传时是否使用超时机制enableLookups="false"--是否反查域名,默认值为 true。为了提高处理能力,应设置为 false compression="on" 打开压缩功能compressionMinSize="10240" 启用压缩的输出内容大小,默认为 2KB noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩 compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
3、Tomcat 有几种部署方式?
1)直接把 Web 项目放在 webapps 下,Tomcat 会自动将其部署
2)在 server.xml 文件上配置节点,设置相关的属性即可
3)通过 Catalina 来进行配置:进入到 conf\Catalina\localhost 文件下,创建一个xml 文件,该文件的名字就是站点的名字。编写 XML 的方式来进行设置。
4、tomcat 容器是如何创建 servlet 类实例?用到了什么原理?
当容器启动时,会读取在 webapps 目录下所有的 web 应用中的 web.xml 文件,然后对 xml 文件进行解析,并读取 servlet 注册信息。然后,将每个应用中注册的 servlet 类都进行加载,并通过反射的方式实例化。(有时候也是在第一次请求时实例化)在 servlet 注册时加上如果为正数,则在一开始就实例化,如果不写或为负数,则第一次请求实例化。
5.tomcat 如何优化?
1、优化连接配置.这里以 tomcat7 的参数配置为例,需要修改 conf/server.xml文件,修改连接数,关闭客户端 dns 查询。参数解释:URIEncoding=”UTF-8″ :使得 tomcat 可以解析含有中文名的文件的 url,真方便,不像 apache 里还有一个 mod_encoding,还要手工编译maxSpareThreads : 如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。minSpareThreads : 最小备用线程数,tomcat 启动时的初始化的线程数。enableLookups : 这个功效和 Apache 中的 HostnameLookups 一样,设为关闭。connectionTimeout : connectionTimeout 为网络连接超时时间毫秒数。maxThreads : maxThreads Tomcat 使用线程来处理接收的每个请求。这个值表示 Tomcat 可创建的最大的线程数,即最大并发数。acceptCount : acceptCount 是当线程数达到 maxThreads 后,后续请求会被放入一个等待队列,这个 acceptCount 是这个队列的大小,如果这个队列也满了,就直接 refuse connection maxProcessors 与 minProcessors : 在 Java 中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出 CPU 最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。通常 Windows 是 1000 个左右,Linux 是 2000 个左右。useURIValidationHack:我们来看一下 tomcat 中的一段源码:
【security】
if (connector.getUseURIValidationHack()) {
String uri = validate(request.getRequestURI());
if (uri == null) {
res.setStatus(400);
res.setMessage(“Invalid URI”);
throw new IOException(“Invalid URI”);
} else {
req.requestURI().setString(uri);
// Redoing the URI decoding
req.decodedURI().duplicate(req.requestURI());
req.getURLDecoder().convert(req.decodedURI(), true);
可以看到如果把 useURIValidationHack 设成”false”,可以减少它对一些 url的不必要的检查从而减省开销。enableLookups=”false” :为了消除 DNS 查询对性能的影响我们可以关闭DNS 查询,方式是修改 server.xml 文件中的 enableLookups 参数值。disableUploadTimeout :类似于 Apache 中的 keeyalive 一样给 Tomcat 配置 gzip 压缩(HTTP 压缩)功能 compression=”on” compressionMinSize=”2048″ compressableMimeType=”text/html,text/xml,text/JavaScript,text/css,text/plain” HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解 压缩并浏览。相对于普通的浏览过程 HTML,CSS,javascript , Text ,它可以节省 40%左右的流量。更为重要的是,它可以对动态生成的,包括 CGI、PHP ,JSP , ASP , Servlet,SHTML 等输出的网页也能进行压缩,压缩效率惊人。
1)compression=”on” 打开压缩功能
2)compressionMinSize=”2048″ 启用压缩的输出内容大小,这里面默认为2KB
3)noCompressionUserAgents=”gozilla, traviata” 对于以下的浏览器,不启用压缩
4)compressableMimeType=”text/html,text/xml” 压缩类型最后不要忘了把 8443 端口的地方也加上同样的配置,因为如果我们走 https 协议的话,我们将会用到 8443 端口这个段的配置,对吧?
<!–enable tomcat ssl–>
<Connector port=”8443″ protocol=”HTTP/1.1″
URIEncoding=”UTF-8″ minSpareThreads=”25″ maxSpareThreads=”
75″
enableLookups=”false” disableUploadTimeout=”true”
connectionTimeout=”20000″
acceptCount=”300″ maxThreads=”300″ maxProcessors=”1000″
minProcessors=”5″
useURIValidationHack=”false”
compression=”on” compressionMinSize=”2048″
compressableMimeType=”text/html,text/xml,text/javascript,text/css,text/plain”
SSLEnabled=”true”
scheme=”https” secure=”true”
clientAuth=”false” sslProtocol=”TLS”
keystoreFile=”d:/tomcat2/conf/shnlap93.jks” keystorePass=”aaaaaa”
/>
好了,所有的 Tomcat 优化的地方都加上了。
6.内存调优
内存方式的设置是在 catalina.sh 中,调整一下 JAVA_OPTS 变量即可,因为后面的启动参数会把 JAVA_OPTS 作为 JVM 的启动参数来处理。具体设置如下:
JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -
XX:NewRatio=4 -XX:SurvivorRatio=4"
其各项参数如下:-Xmx3550m:设置 JVM 最大可用内存为 3550M。
-Xms3550m:设置 JVM 促使内存为 3550m。此值可以设置与-Xmx 相同,以避免每次垃圾回收完成后 JVM 重新分配内存。
-Xmn2g:设置年轻代大小为 2G。整个堆大小=年轻代大小 + 年老代大小 +持久带大小。持久带一般固定大小为 64m,所以增大年轻代后,将会减小年老 代大小。此值对系统性能影响较大,Sun 官方推荐配置为整个堆的 3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右。
-XX:NewRatio=4:设置年轻代(包括 Eden 和两个 Survivor 区)与年老代的比值(除去持久代)。设置为 4,则年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5
-XX:SurvivorRatio=4:设置年轻代中 Eden 区域 Survivor 区的大小比值。设置为 4,则两个 Survivor 区域一个 Eden 区的比值为 2:4,一个Survivor 区占整个年轻代的 1/6 -XX:MaxPermSize=16m:设置持久代大小为 16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
7.垃圾回收策略调优
垃圾回收的设置也是在 catalina.sh 中,调整 JAVA_OPTS 变量。具体设置如下:
JAVA_OPTS="$JAVA_OPTS -Xmx3550m -Xms3550m -Xss128k -
XX:+UseParallelGC -XX:MaxGCPauseMillis=100"
具体的垃圾回收策略及相应策略的各项参数如下:串行收集器(JDK1.5 以前主要的回收方式) -XX:+UseSerialGC:设置串行收集器 并行收集器(吞吐量优先) 示例:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-XX:+UseParallelGC
- XX:MaxGCPauseMillis=100
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0 支持对年老代并行收集
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM 会自动调整年轻代大小,以满足此值。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的 Survivor 区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。并发收集器(响应时间优先) 示例:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
- XX:+UseConcMarkSweepGC
-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,
-XX:NewRatio=4 的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn 设置。
-XX:+UseParNewGC: 设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,所以无需再设置此值。
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次 GC 以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片
8.共享 session 处理
目前的处理方式有如下几种:
1).使用 Tomcat 本身的 Session 复制功能 参考 http://ajita.iteye.com/blog/1715312(Session 复制的配置)方案的有点是配置简单,缺点是当集群数量较多时,Session 复制的时间会比 较长,影响响应的效率
2).使用第三方来存放共享 Session 目前用的较多的是使用 memcached 来管理共享 Session,借助于memcached-sesson-manager 来进行 Tomcat 的 Session 管理参考 http://ajita.iteye.com/blog/1716320(使用 MSM 管理 Tomcat 集群session)
3).使用黏性 session 的策略 对于会话要求不太强(不涉及到计费,失败了允许重新请求下等)的场合,同一个用户的 session 可以由 nginx 或者 apache 交给同一个 Tomcat 来处理,这就是所谓的 session sticky 策略,目前应用也比较多 参考:http://ajita.iteye.com/blog/1848665(tomcat session sticky)nginx 默认不包含 session sticky 模块,需要重新编译才行 优点是处理效率高多了,缺点是强会话要求的场合不合适
8.添加 JMS 远程监控
对于部署在局域网内其它机器上的 Tomcat,可以打开 JMX 监控端口,局域网其它机器就可以通过这个端口查看一些常用的参数(但一些比较复杂的功能不支持),同样是在 JVM 启动参数中配置即可,配置如下:-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.71.38 设置 JVM 的 JMS 监控监听的 IP地址,主要是为了防止错误的监听成 127.0.0.1 这个内网地址 -Dcom.sun.management.jmxremote.port=1090 设置 JVM 的 JMS 监控的端口 -Dcom.sun.management.jmxremote.ssl=false 设置 JVM 的 JMS 监控不实用SSL -Dcom.sun.management.jmxremote.authenticate=false 设置 JVM 的 JMS 监控不需要认证
9.专业点的分析工具有
IBM ISA,JProfiler、probe 等,具体监控及分析方式去网上搜索即可
10.关于 Tomcat 的 session 数目
这个可以直接从 Tomcat 的 web 管理界面去查看即可 ;或者借助于第三方工具 Lambda Probe 来查看,它相对于 Tomcat 自带的管理稍微多了点功能,但也不多 ;
11.监视 Tomcat 的内存使用情况
使用 JDK 自带的 jconsole 可以比较明了的看到内存的使用情况,线程的状态,当前加载的类的总量等;JDK 自带的 jvisualvm 可以下载插件(如 GC 等),可以查看更丰富的信息。如果是分析本地的 Tomcat 的话,还可以进行内存抽样等,检查每个类的使用情况
12.打印类的加载情况及对象的回收情况
这个可以通过配置 JVM 的启动参数,打印这些信息(到屏幕(默认也会到 catalina.log 中)或者文件),具体参数如下:
-XX:+PrintGC:输出形式:[GC 118250K->113543K(130112K), 0.0094143secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintGCDetails:输出形式:[GC [DefNew: 8614K->781K(9088K),0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K),0.0436268 secs]
-XX:+PrintGCTimeStamps
-XX:+PrintGC:PrintGCTimeStamps 可与上面两个混合使用,输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960secs]
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用。输出形式:Application time: 0.5291524seconds
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用。输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:PrintHeapAtGC: 打印 GC 前后的详细堆栈信息
-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析 -verbose:class 监视加载的类的情况 -verbose:gc 在虚拟机发生内存回收时在输出设备显示信息 -verbose:jni 输出 native 方法调用的相关情况,一般用于诊断 jni 调用错误信息
13.Tomcat 一个请求的完整过程
Ng:(nginx)
upstream yy_001{
server 10.99.99.99:8080;
server 10.99.99.100:8080;
hash $**;
healthcheck_enabled;
healthcheck_delay 3000;
healthcheck_timeout 1000;
healthcheck_failcount 2;
healthcheck_send 'GET /healthcheck.html HTTP/1.0' 'Host: wo.com'
'Connection: close';
}
server {
include base.conf;
server_name wo.de.tian;
...
location /yy/ {
proxy_pass http://yy_001;
}
首先 dns 解析 wo.de.tian 机器,一般是 ng 服务器 ip 地址 然后 ng 根据 server 的配置,寻找路径为 yy/的机器列表,ip 和端口 最后 选择其中一台机器进行访问—->下面为详细过程
1) 请求被发送到本机端口 8080,被在那里侦听的 Coyote HTTP/1.1Connector 获得
2) Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待来自Engine 的回应
3) Engine 获得请求 localhost/yy/index.jsp,匹配它所拥有的所有虚拟主机 Host
4) Engine 匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该 Host处理,因为该 Host 被定义为该 Engine 的默认主机)
5) localhost Host 获得请求/yy/index.jsp,匹配它所拥有的所有 Context
6) Host 匹配到路径为/yy 的 Context(如果匹配不到就把该请求交给路径名为”“的 Context 去处理)
7) path=”/yy”的 Context 获得请求/index.jsp,在它的 mapping table 中寻找对应的 servlet
8) Context 匹配到 URL PATTERN 为*.jsp 的 servlet,对应于 JspServlet 类
9) 构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用JspServlet 的 doGet 或 doPost 方法
10)Context 把执行完了之后的 HttpServletResponse 对象返回给 Host
11)Host 把 HttpServletResponse 对象返回给 Engine
12)Engine 把 HttpServletResponse 对象返回给 Connector
13)Connector 把 HttpServletResponse 对象返回给客户 browser
14.Tomcat 工作模式?
Tomcat 是一个 JSP/Servlet 容器。其作为 Servlet 容器,有三种工作模式:独立的 Servlet 容器、进程内的 Servlet 容器和进程外的 Servlet 容器。
进入 Tomcat 的请求可以根据 Tomcat 的工作模式分为如下两类:Tomcat 作为应用程序服务器:请求来自于前端的 web 服务器,这可能是Apache, IIS, Nginx 等;Tomcat 作为独立服务器:请求来自于 web 浏览器;
15.你怎样给 tomcat 去调优?
JVM 参数调优:-Xms 表示 JVM 初始化堆的大小,-Xmx表示 JVM 堆的最大值。这两个值的大小一般根据需要进行设置。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的 80%。在 catalina.bat 中,设置 JAVA_OPTS='-Xms256m -Xmx512m',表示初始化内存为 256MB,可以使用的最大内存为 512MB。
禁用 DNS 查询 当 web 应用程序向要记录客户端的信息时,它也会记录客户端的 IP 地址或者通过域名服务器查找机器名转换为 IP 地址。DNS 查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的 IP 的过程,这样会消耗一定的时间。为了消除 DNS 查询对性能的影响我们可以关闭 DNS 查询,方式是修改 server.xml 文件中的 enableLookups 参数值:
Tomcat4 Tomcat5
调整线程数 通过应用程序的连接器(Connector)进行性能控制的的参数是创建的处理请求的线程数。Tomcat 使用线程池加速响应速度来处理请求。在 Java 中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出 CPU 最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。Tomcat4 中可以通过修改 minProcessors 和 maxProcessors 的值来控制线程数。这些值在安装后就已经设定为默认值并且是足够使用的,但是随着站点的扩容而改大这些值。minProcessors 服务器启动时创建的处理请求的线程数应该足够处理一个小量的负载。也就是说,如果一天内每秒仅发生 5 次单击事件,并且每个请求任务处理需要 1 秒钟,那么预先设置线程数为 5 就足够了。但在你的站点访问量较大时就需要设置更大的线程数,指定为参数maxProcessors 的值。maxProcessors 的值也是有上限的,应防止流量不可控制(或者恶意的服务攻击),从而导致超出了虚拟机使用内存的大小。如果要加大并发连接数,应同时加大这两个参数。web server 允许的最大连接数还受制于操作系统的内核参数设置,通常Windows 是 2000 个左右,Linux 是 1000 个左右。在 Tomcat5 对这些参数进行了调整,请看下面属性:maxThreads Tomcat 使用线程来处理接收的每个请求。这个值表示 Tomcat 可创建的最大的线程数。acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。connnectionTimeout 网络连接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为 30000 毫秒。minSpareThreads Tomcat 初始化时创建的线程数。maxSpareThreads 一旦创建的线程超过这个值,Tomcat 就会关闭不再需要的 socket 线程。最好的方式是多设置几次并且进行测试,观察响应时间和内存使用情况。在不同的机器、操作系统或虚拟机组合的情况下可能会不同,而且并不是所有人的 web 站点的流量都是一样的,因此没有一刀切的方案来确定线程数的值。
16.如何加大 tomcat 连接数
在 tomcat 配置文件 server.xml 中的配置中,和连接数相关的参数有:minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为 10 maxProcessors:最大连接线程数,即:并发处理的最大请求数,默认值为 75 acceptCount:允许的最大连接数,应大于等于 maxProcessors,默认值为 100 enableLookups:是否反查域名,取值为:true 或 false。为了提高处理能力,应设置为 false connectionTimeout:网络连接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为 30000 毫秒。其中和最大连接数相关的参数为 maxProcessors 和 acceptCount。如果要加大并发连接数,应同时加大这两个参数。web server 允许的最大连接数还受制于操作系统的内核参数设置,通常 Windows 是 2000 个左右,Linux 是 1000 个左右。tomcat5 中的配置示例:
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" />
对于其他端口的侦听配置,以此类推。
3. tomcat 中如何禁止列目录下的文件
在{tomcat_home}/conf/web.xml 中,把 listings 参数设置成 false 即可,如下:
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
17.怎样加大 tomcat 的内存。
首先检查程序有没有陷入死循环 这个问题主要还是由这个问题 java.lang.OutOfMemoryError: Java heap space 引起的。第一次出现这样的的问题以后,引发了其他的问题。在网上一查可能是 JAVA 的堆栈设置太小的原因。跟据网上的答案大致有这两种解决方法:
1、设置环境变量 解决方法:手动设置 Heap size 修改 TOMCAT_HOME/bin/catalina.sh set JAVA_OPTS= -Xms32m -Xmx512m 可以根据自己机器的内存进行更改。
2、java -Xms32m -Xmx800m className 就是在执行 JAVA 类文件时加上这个参数,其中 className 是需要执行的确类名。(包括包名) 这个解决问题了。而且执行的速度比没有设置的时候快很多。如果在测试的时候可能会用 Eclispe 这时候就需要在 Eclipse ->run -arguments 中的 VM arguments 中输入-Xms32m -Xmx800m 这个参数就可以了。后来在 Eclilpse 中修改了启动参数,在 VM arguments 加入了-Xms32m -Xmx800m,问题解决。一、java.lang.OutOfMemoryError: PermGen space PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域,这块内存主要是被 JVM 存放 Class 和 Meta 信息的,Class 在被 Loader 时就会被放到 PermGen space 中,它和存放类实例(Instance)的 Heap 区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space 进行清理,所以如果你的应用中有很多 CLASS 的话,就很可能出现 PermGen space 错误,这种错误常见在 web 服务器对 JSP 进行 pre compile 的时候。如果你的 WEB APP 下都用了大量的第三方 jar, 其大小超过了 jvm 默认的大小(4M)那么就会产生此错误信息了。解决方法:手动设置 MaxPermSize 大小 修改 TOMCAT_HOME/bin/catalina.sh在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m
建议:将相同的第三方 jar 文件移置到 tomcat/shared/lib 目录下,这样可以达到减少 jar 文档重复占用内存的目的。二、java.lang.OutOfMemoryError: Java heap spaceHeap size 设置 JVM 堆的设置是指 java 程序运行过程中 JVM 可以调配使用的内存空间的设置.JVM 在启动的时候会自动设置 Heap size 的值, 其初始空间(即-Xms)是物理内存的 1/64,最大空间(-Xmx)是物理内存的 1/4。可以利用 JVM 提供的-Xmn -Xms -Xmx 等选项可进行设置。Heap size 的大小是 Young Generation 和 Tenured Generaion 之和。提示:在 JVM 中如果 98%的时间是用于 GC 且可用的 Heap size 不足 2%的时候将抛出此异常信息。提示:Heap Size 最大不要超过可用物理内存的 80%,一般的要将-Xms 和-Xmx 选项设置为相同,而Xmn 为 1/4 的-Xmx 值。解决方法:手动设置 Heap size 修改 TOMCAT_HOME/bin/catalina.sh 在“echo "Using CATALINA_BASE: $CATALINA_BASE"”上面加入以下行:
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:MaxNewSize=256m"
三、实例,以下给出 1G 内存环境下 java jvm 的参数设置参考:JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=64M -XX:MaxNewSize=256m - XX:MaxPermSize=128m -Djava.awt.headless=true " 很大的 web 工程,用 tomcat 默认分配的内存空间无法启动,如果不是在 myeclipse 中启动 tomcat 可以对 tomcat 这样设置:TOMCAT_HOME/bin/catalina.bat 中添加这样一句话:set JAVA_OPTS=-server -Xms2048m -Xmx4096m -XX:PermSize=512M - XX:MaxPermSize=1024M -Duser.timezone=GMT+08 或者 set JAVA_OPTS= -Xmx1024M -Xms512M -XX:MaxPermSize=256m 如果要在 myeclipse 中启动,上述的修改就不起作用了,可如下设置:Myeclipse->preferences->myeclipse->servers->tomcat->tomcat×.×->JDK 面板中的Optional Java VM arguments 中添加:-Xmx1024M -Xms512M -XX:MaxPermSize=256m 以上是转贴,但本人遇见的问题是:在 myeclipse 中启动 Tomcat 时,提示"ava.lang.OutOfMemoryError: Java heap space",解决办法就是:Myeclipse->preferences->myeclipse->servers->tomcat->tomcat×.×->JDK 面板中的Optional Java VM arguments 中添加:-Xmx1024M -Xms512M -XX:MaxPermSize=256m
18.Tomcat 有几种部署方式
tomcat 中四种部署项目的方法 第一种方法:在 tomcat 中的 conf 目录中,在 server.xml 中的,节点中添加:
<Context path="/hello" docBase="D:/eclipse3.2.2/forwebtoolsworkspacehello/WebRoot" debug="0" privileged="true">
</Context>
至于 Context 节点属性,可详细见相关文档。第二种方法:将 web 项目文件件拷贝到 webapps 目录中。第三种方法:很灵活,在 conf 目录中,新建 Catalina(注意大小写)\localhost 目录,在该目录中新建一个 xml 文件,名字可以随意取,只要和当前文件中的文件名不重复就行了,该 xml 文件的内容为:
<Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true">
</Context>
第 3 个方法有个优点,可以定义别名。服务器端运行的项目名称为 path,外部访问的 URL 则使用 XML 的文件名。这个方法很方便的隐藏了项目的名称,对一些项目名称被固定不能更换,但外部访问时又想换个路径,非常有效。第 2、3 还有优点,可以定义一些个性配置,如数据源的配置等。第四种办法,: 可以用 tomcat 在线后台管理器,一般 tomcat 都打开了,直接上传 war 就可以
19.Tomcat 的优化经验。
Tomcat 作为 Web 服务器,它的处理性能直接关系到用户体验,下面是几种常见的优化措施:
去掉对 web.xml 的监视,把 jsp 提前编辑成 Servlet。有富余物理内存的情况,加大 tomcat 使用的 jvm 的内存。
服务器资源 服务器所能提供 CPU、内存、硬盘的性能对处理能力有决定性影响。o 对于高并发情况下会有大量的运算,那么 CPU 的速度会直接影响到处理速度。
o 内存在大量数据处理的情况下,将会有较大的内存容量需求,可以用 -Xmx -Xms -XX:MaxPermSize 等参数对内存不同功能块进行划分。我们之前就遇到过内存分配不足,导致虚拟机一直处于 full GC,从而导致处理能力严重下降。
o 硬盘主要问题就是读写性能,当大量文件进行读写时,磁盘极容易成为性能瓶颈。最好的办法还是利用下面提到的缓存。
利用缓存和压缩对于静态页面最好是能够缓存起来,这样就不必每次从磁盘上读。这里我们采用了 Nginx 作为缓存服务器,将图片、css、js 文件都进行了缓存,有效的减少了后端 tomcat 的访问。另外,为了能加快网络传输速度,开启gzip 压缩也是必不可少的。但考虑到 tomcat 已经需要处理很多东西了,所以把这个压缩的工作就交给前端的 Nginx 来完成。除了文本可以用gzip 压缩,其实很多图片也可以用图像处理工具预先进行压缩,找到一个平衡点可以让画质损失很小而文件可以减小很多。曾经我就见过一个图片从 300多 kb 压缩到几十 kb,自己几乎看不出来区别。
采用集群 单个服务器性能总是有限的,最好的办法自然是实现横向扩展,那么组建tomcat 集群是有效提升性能的手段。我们还是采用了 Nginx 来作为请求分 流的服务器,后端多个 tomcat 共享 session 来协同工作。可以参考之前写的《利用 nginx+tomcat+memcached 组建 web 服务器负载均衡》。
优化 tomcat 参数 这里以 tomcat7 的参数配置为例,需要修改 conf/server.xml 文件,主要是优化连接配置,关闭客户端 dns 查询。
此博客文章中,我想分享一个强大的工具Leadership Health Check。这将帮助您的管理团队变得更强大,并为积极的服务型领导团队揭示改进机会,从而更好地赋能您所支持的敏捷团队。
首先,让我们从头开始。
在敏捷教练的工具箱中,我最喜欢的一项练习是在Spotify工作期间学到的 Squad Health Check (中文版:https://www.bobjiang.com/posts/blog/sqad_health_check_model.html) 。这是一种以回顾的形式进行自我评估的研讨会。在会上,团队表达自己在各种主题上的感受,例如协作,交付的价值,影响力,获得组织的支持等。结果会生成对团队和领导力的洞见及改进措施。我喜欢这个工具,因为它是加强自组织,组织文化和持续学习的非常棒的工具。
一年多以前, 我和Spotify 的一位同事Georgiana Laura Levinta为我们的tribe创建了领导力健康检查(tribe是Spotify的半自治部门,由4-8个团队组成,有一组专门的leader和经理)(更多有关tribe可以参考 https://www.bobjiang.com/posts/blog/scaling-agile-spotify-with-tribes-squads-chapters-guilds.html) 。
我和Geo受到了Squad Health Check的启发,采用这种做法帮助tribe的管理者自我评估他们向tribe内的squad提供积极支持的领导能力,并讨论他们如何作为一个团队进行改进,以提供更好的支持。
从那时起,我和Casumo客户一起基于他们的背景、文化和信仰采用了这一方法。我们已经在公司的领导团队以及tribe级别(半自治部门)实践了几次,获得了巨大的成功和价值。我相信 Team Health Check和 Leadership Health Check都非常强大;因此,我想将它们推荐到更广泛的敏捷社区,希望更多的组织会发现它们的价值,或者至少受到他们的启发,然后进行完全不同的尝试。
如果您不想了解团队和领导力健康检查背后的起源和思考,而只是来这里下载研讨会材料,请关注本公众号回复"检查表"获取下载地址。
团队健康度检查表
(Spotify的 Suard Health Check)
Spotify的Suard Health Check的第一个版本看起来与今天的版本有很大不同。它提出了诸如"您有产品负责人PO吗?","您有敏捷教练的支持吗?"和"发布容易吗?" 之类的问题。随着这些组织上的痛点(例如,并非所有的团队都有PO)逐渐消失,该调查逐渐发展为更加关注自组织,团队合作,可持续流程和任务明确性(见右侧示例)。
这种形式在Spotify内部迅速传播开来,越来越多的团队和tribe'使用这个。如今,这已成为许多组织中根深蒂固的习惯。团队每年进行两次到四次check。当每个tribe根据其背景和需求采用它并与其他tribe共享其版本时,该工具得到不断发展。
The Team Health Check
我在这篇博客文章中分享的 Team Health Check的灵感来自Spotify的 Squad Health Check。通过与其他客户的合作,我对其进行了改进,使其更加通用。
现在对各种主题和评论的阐述,试图嵌入呈现一些著作的思考和研究:如 Christopher Avery的Teamwork is an Individual Skill , J. Richard Hackman的 Leading Teams ,Daniel H. Pink,的 Drive , Stanley McChrystal的 Team of Teams , Patrick Lencioni的 Google's Aristotle Project和The Five Dysfunctions of a Team ,还有甚多其他书籍和敏捷领袖的思想研究。
The Team Health Check 包含大约12个主题。对于每个主题,都有绿色和红色的说明。绿色表示可观察到的健康或积极的案例。红色表示不健康或不良案例。这里有两个例子:
1)团队自组织
2)反馈
如果你要在你的组织中与团队一起实践" Team Health Check",我强烈建议你基于组织的文化和背景进行修改采用,并决定模板中哪些可以保留。我相信这是一次伟大的领导力实践。定义主题和声明所面临的挑战迫使你们作为领导者聚焦在你们渴望建立的文化,想要看到的行为以及预期的与领导力和组织期望达成共识的成员。
由于团队可能每年进行几次健康检查,因此在某种程度上这可以作为您所追求的目标教育,并且在某种意义上成为团队的权利清单。如果他们每次都收获了问题,每次都感到有能力操纵和打造计划和命运,他们便找到了应该的方式。如果被问及他们是否有时间提前思考和尝试,则意味着应该允许他们有足够的时间来这样做。等等。
以我的经验,每年与团队进行两次到四次 Team Health Check,你将收获:
如果您作为团队成员害怕或注意到,自我评估的结果已被管理层用来比较不同团队有多"好"--那么不要在团队之外分享您的结果!与其他任何回顾会形式一样,将其用作产生见解(洞见),讨论和行动的工具。分享行动,结果,而不是讨论本身。
如果您是经理,想使用该工具评估团队的"敏捷成熟度",效能或在团队之间进行比较-请勿这么做!答案将非常主观,去反映每个团队的具体情况和挑战。如果想评估的话,实践的结果可以表明您在为团队提供支持方面做得怎样。这不是衡量团队表现的如何,而是衡量您作为领导者的表现如何。
领导力健康检查表
领导力健康检查的目的与团队健康检查的目的相同,可以自我评估我们在各个领域的团队感觉如何,并揭示我们如何改善协作和交付的价值。在这种情况下,团队是一组敏捷团队的经理和领导者。我们提供的价值是我们为团队提供的支持,以及我们为帮助团队为用户和利益相关者提供价值所做的工作。
(领导健康检查练习投票的结果)
正如我在本文开头所提到的,领导力健康检查的第一版是与Spotify的同事Georgiana Laura Levinta共同开发的。Georgiana还写了一篇有关该工具创建的Spotify内部博客文章。希望有一天它将在Spotify的官方博客上发布。
我在这里共享的版本是我与当前客户端Casumo一起运行的版本。它引发了有关敏捷环境中领导力的大量讨论。它凝聚了领导团队,激发了行动和变化,以进一步改善他们支持团队的方式。
实践证明,它对于公司的领导团队(由CEO,CTO,CFO等组成)以及部门级领导均有效。研讨会产生了见识,并指导领导团队确定下一步的工作重点。
专为管理/领导团队设计
该领导健康检查承认领导者/管理者团队不同于紧密结合起来,推出产品或者提供服务的团队。他们可能不会为实现短期目标而协作,将他们团结在一起的是他们所提供的服务:提供支持,指导,指引和团队可以在其中蓬勃发展和脱颖而出的环境。为了有效地做到这一点,他们需要将自己视为一个团队,并与价值观和长期战略保持一致,就如何做出决定以及什么构成良好的领导达成共识。
其中一些主题与团队健康检查的主题相同,例如信任与安全,可靠性,持续改进和反馈。其他是领导力健康检查特有的,例如文化与价值观,愿景与方向,服务型领导力和透明度。
采用领导力健康检查
我的建议与团队健康检查的建议相同,如果您要在部门或公司中运用"领导者健康检查",我强烈建议基于文化和信任的基础将其用在检查领导力水平上。
在一组主题和声明上达成一致是一项很棒的练习,并且很可能会引发大量艰难而健康的讨论,迫使您调整并决定对您而言重要的事情。例如,您认为自己是一个团队还是一个松散的一组人?您相信包容性的决策还是明确的决策?您对这些问题的立场应反映在健康检查中。
实践社区健康检查
这是一个惊喜奖励。当我与客户一起工作时,我接触了高端人才。人才在他们的组织是一组具备相同信仰和能力的人,有时也被称为实践社区。在Spotify这是分队(Chapter)。我们开发了健康检查,其主题针对实践社区成员彼此之间的需求进行了调整。他们通常不像团队那样朝着一个共同的目标努力,但是他们确实共享其他需求,例如知识共享,帮助,与某些长期战略保持一致等等。
主题和声明未经过修饰,也没有经过许多次改进(如果有的话)。但是,如果需要,可以关注本公众号回复"检查表"下载。也许您可以将其用作灵感来源。
引导
我以几乎完全相同的方式引导团队和领导力检查。我通常计划90分钟的会议,并为研讨会作如下安排:
1)欢迎-- 5分钟
研讨会的目的及其结构的说明。我强调自我评估的方式,是一种回顾的形式,而不是从任何客观角度衡量我们作为一个团队的好坏的方法。
2)自我评估-15分钟
对于每个主题,我要求某人大声朗读主题,绿色和红色声明。然后每个人投票。如果您认为绿色最能说明事情的发展,请投绿色票。如果您认为红色最能说明事情的发展,请选择红色。如果您同时看到绿色和红色示例,或者认为事物既不是绿色也不是红色,则将其投票为黄色。收集投票,然后进入下一个主题,请下一个人大声朗读,依此类推。
注意:在一起做这件事时,我要求人们用绿色,黄色或红色的便利贴来投票。然后,我收集便签纸并将其放在白板上。在与远程成员一起为研讨会提供便利时,我准备了一个Google Spreadsheet,然后请与会人员对文档进行投票。在下面的屏幕快照中,您可以同时看到团队成员的投票以及在第4步之后的讨论中捕获的评论。
3)反思-- 5分钟
我要求大家简短地评论并反思总体结果。- 5分钟
4)探寻改进措施-- 45分钟
找到并讨论最有趣的结果。可能是投票最多的红色话题,或者是投票范围最广的绿色,黄色,红色话题,我们对该话题的看法截然不同。我试图促进讨论,以便提出建议和决定并采取行动。
5)摘要-- 5分钟
重复商定的行动要点及责任人。
确保分配了某人来记录并共享结果。
6)结束-- 5分钟
结束环节。我通常请每个人简短地评论"您认为对于我们团队来说,最重要的讨论或行动是什么?"
常见问题
*问:为了使答案更全面,此练习是否需要团队成员之间的信任? *
是。但是我也相信做这样的练习可以建立信任。随着团队成员认识到这不是评估他们的工具,而是帮助他们自己变得更强大的工具,答案将更加诚实。结果,随后的讨论产生了更深刻的见解以及更具影响力的行动和变化。
问:一个部门汇总几个团队的团队健康检查结果没有价值吗?
当然。如果您作为领导者想查看模式和趋势,以了解应该将精力集中在哪里,那么这将非常有价值。上图显示了Spotify中的真实示例。例如,这有助于领导者和团队确定工作的优先级,以使其易于发布。第4小队是在两周前成立的,所以他们的自我意识可能真的很低,因为一切看上去都很绿色。第2小队似乎在挣扎很多。因此,这两个小队得到了额外的支持和照顾。
问:但是我真的很想用它来比较不同团队的表现。我为什么不能?
不,你不能!有几个原因。这是一种自我评估,因此结果会从团队的个人角度揭示团队的敬业精神。一个小队可能会觉得他们的移动速度非常快,而另一个小队却非常缓慢,即使它们都以每周一次的节奏进行释放。每个团队的背景和挑战差异很大。一个团队可能将每日更新发布到网站,而另一个团队则在许多外部依赖项下苦苦挣扎。一个团队可能很大,另一个可能很小。等等。
如果您将其用于比较,甚至可能用于决定要奖励的团队,那么团队会清楚这一点,并开始以不诚实的答案来计算结果,以"看起来不错"。
问:结果应该共享吗?
我坚信透明度,所以如果您问我-是的。不过,我的建议是仅分享结果并商定行动,而不是由谁对每个主题投票。透明并共享结果可以建立信任,实现跨团队学习并培养责任感。
问:我觉得有些问题太含糊,太开放,将其划分为更多的问题并缩小范围会怎样?
当然可以。如果这些问题不能引发良好的讨论不能提供有用的见解,请尝试其他问题。但是我的建议是提出的问题不要超过十二至十四个。如果添加一个,也许应该删除另一个。问:为了节省时间,我是否应该在研讨会之前发送问题并请人们投票?
也许这是一个很大的群体,例如大于10人。否则,我觉得在研讨会中一起做起来会更加有乐趣和参与感。
关注本公众号,查看图片。 回复"检查表"获取下载地址。不要犹豫,复制并采用它们基于您的上下文,然后与您的团队一起使用。
译者:Fish
审校:姜信宝
原文:https://blog.crisp.se/2019/03/11/jimmyjanlen/health-checks-for-teams-and-leadership
本文首发于 Bob Jiang的博客 ,转载请联系 Bob Jiang
*请认真填写需求信息,我们会在24小时内与您取得联系。